home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / MDB2.php < prev    next >
Encoding:
PHP Script  |  2006-04-07  |  109.3 KB  |  3,420 lines

  1. <?php
  2. // vim: set et ts=4 sw=4 fdm=marker:
  3. // +----------------------------------------------------------------------+
  4. // | PHP versions 4 and 5                                                 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
  7. // | Stig. S. Bakken, Lukas Smith                                         |
  8. // | All rights reserved.                                                 |
  9. // +----------------------------------------------------------------------+
  10. // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
  11. // | API as well as database abstraction for PHP applications.            |
  12. // | This LICENSE is in the BSD license style.                            |
  13. // |                                                                      |
  14. // | Redistribution and use in source and binary forms, with or without   |
  15. // | modification, are permitted provided that the following conditions   |
  16. // | are met:                                                             |
  17. // |                                                                      |
  18. // | Redistributions of source code must retain the above copyright       |
  19. // | notice, this list of conditions and the following disclaimer.        |
  20. // |                                                                      |
  21. // | Redistributions in binary form must reproduce the above copyright    |
  22. // | notice, this list of conditions and the following disclaimer in the  |
  23. // | documentation and/or other materials provided with the distribution. |
  24. // |                                                                      |
  25. // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
  26. // | Lukas Smith nor the names of his contributors may be used to endorse |
  27. // | or promote products derived from this software without specific prior|
  28. // | written permission.                                                  |
  29. // |                                                                      |
  30. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
  31. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
  32. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
  33. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
  34. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
  35. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  36. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  37. // |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
  38. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
  39. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  40. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
  41. // | POSSIBILITY OF SUCH DAMAGE.                                          |
  42. // +----------------------------------------------------------------------+
  43. // | Author: Lukas Smith <smith@pooteeweet.org>                           |
  44. // +----------------------------------------------------------------------+
  45. //
  46. // $Id: MDB2.php,v 1.163 2006/02/09 12:45:30 lsmith Exp $
  47. //
  48.  
  49. /**
  50.  * @package  MDB2
  51.  * @category Database
  52.  * @author   Lukas Smith <smith@pooteeweet.org>
  53.  */
  54.  
  55. require_once 'PEAR.php';
  56.  
  57. /**
  58.  * The method mapErrorCode in each MDB2_dbtype implementation maps
  59.  * native error codes to one of these.
  60.  *
  61.  * If you add an error code here, make sure you also add a textual
  62.  * version of it in MDB2::errorMessage().
  63.  */
  64.  
  65. define('MDB2_OK',                      true);
  66. define('MDB2_ERROR',                     -1);
  67. define('MDB2_ERROR_SYNTAX',              -2);
  68. define('MDB2_ERROR_CONSTRAINT',          -3);
  69. define('MDB2_ERROR_NOT_FOUND',           -4);
  70. define('MDB2_ERROR_ALREADY_EXISTS',      -5);
  71. define('MDB2_ERROR_UNSUPPORTED',         -6);
  72. define('MDB2_ERROR_MISMATCH',            -7);
  73. define('MDB2_ERROR_INVALID',             -8);
  74. define('MDB2_ERROR_NOT_CAPABLE',         -9);
  75. define('MDB2_ERROR_TRUNCATED',          -10);
  76. define('MDB2_ERROR_INVALID_NUMBER',     -11);
  77. define('MDB2_ERROR_INVALID_DATE',       -12);
  78. define('MDB2_ERROR_DIVZERO',            -13);
  79. define('MDB2_ERROR_NODBSELECTED',       -14);
  80. define('MDB2_ERROR_CANNOT_CREATE',      -15);
  81. define('MDB2_ERROR_CANNOT_DELETE',      -16);
  82. define('MDB2_ERROR_CANNOT_DROP',        -17);
  83. define('MDB2_ERROR_NOSUCHTABLE',        -18);
  84. define('MDB2_ERROR_NOSUCHFIELD',        -19);
  85. define('MDB2_ERROR_NEED_MORE_DATA',     -20);
  86. define('MDB2_ERROR_NOT_LOCKED',         -21);
  87. define('MDB2_ERROR_VALUE_COUNT_ON_ROW', -22);
  88. define('MDB2_ERROR_INVALID_DSN',        -23);
  89. define('MDB2_ERROR_CONNECT_FAILED',     -24);
  90. define('MDB2_ERROR_EXTENSION_NOT_FOUND',-25);
  91. define('MDB2_ERROR_NOSUCHDB',           -26);
  92. define('MDB2_ERROR_ACCESS_VIOLATION',   -27);
  93. define('MDB2_ERROR_CANNOT_REPLACE',     -28);
  94. define('MDB2_ERROR_CONSTRAINT_NOT_NULL',-29);
  95. define('MDB2_ERROR_DEADLOCK',           -30);
  96. define('MDB2_ERROR_CANNOT_ALTER',       -31);
  97. define('MDB2_ERROR_MANAGER',            -32);
  98. define('MDB2_ERROR_MANAGER_PARSE',      -33);
  99. define('MDB2_ERROR_LOADMODULE',         -34);
  100. define('MDB2_ERROR_INSUFFICIENT_DATA',  -35);
  101.  
  102. /**
  103.  * These are just helper constants to more verbosely express parameters to prepare()
  104.  */
  105.  
  106. define('MDB2_PREPARE_MANIP', false);
  107. define('MDB2_PREPARE_RESULT', null);
  108.  
  109. /**
  110.  * This is a special constant that tells MDB2 the user hasn't specified
  111.  * any particular get mode, so the default should be used.
  112.  */
  113.  
  114. define('MDB2_FETCHMODE_DEFAULT', 0);
  115.  
  116. /**
  117.  * Column data indexed by numbers, ordered from 0 and up
  118.  */
  119.  
  120. define('MDB2_FETCHMODE_ORDERED', 1);
  121.  
  122. /**
  123.  * Column data indexed by column names
  124.  */
  125.  
  126. define('MDB2_FETCHMODE_ASSOC', 2);
  127.  
  128. /**
  129.  * Column data as object properties
  130.  */
  131.  
  132. define('MDB2_FETCHMODE_OBJECT', 3);
  133.  
  134. /**
  135.  * For multi-dimensional results: normally the first level of arrays
  136.  * is the row number, and the second level indexed by column number or name.
  137.  * MDB2_FETCHMODE_FLIPPED switches this order, so the first level of arrays
  138.  * is the column name, and the second level the row number.
  139.  */
  140.  
  141. define('MDB2_FETCHMODE_FLIPPED', 4);
  142.  
  143. // }}}
  144. // {{{ portability modes
  145.  
  146. /**
  147.  * Portability: turn off all portability features.
  148.  * @see MDB2_Driver_Common::setOption()
  149.  */
  150.  
  151. define('MDB2_PORTABILITY_NONE', 0);
  152.  
  153. /**
  154.  * Portability: convert names of tables and fields to case defined in the
  155.  * "field_case" option when using the query*(), fetch*() and tableInfo() methods.
  156.  * @see MDB2_Driver_Common::setOption()
  157.  */
  158.  
  159. define('MDB2_PORTABILITY_FIX_CASE', 1);
  160.  
  161. /**
  162.  * Portability: right trim the data output by query*() and fetch*().
  163.  * @see MDB2_Driver_Common::setOption()
  164.  */
  165.  
  166. define('MDB2_PORTABILITY_RTRIM', 2);
  167.  
  168. /**
  169.  * Portability: force reporting the number of rows deleted.
  170.  * @see MDB2_Driver_Common::setOption()
  171.  */
  172.  
  173. define('MDB2_PORTABILITY_DELETE_COUNT', 4);
  174.  
  175. /**
  176.  * Portability: not needed in MDB2 (just left here for compatibility to DB)
  177.  * @see MDB2_Driver_Common::setOption()
  178.  */
  179.  
  180. define('MDB2_PORTABILITY_NUMROWS', 8);
  181.  
  182. /**
  183.  * Portability: makes certain error messages in certain drivers compatible
  184.  * with those from other DBMS's.
  185.  *
  186.  * + mysql, mysqli:  change unique/primary key constraints
  187.  *   MDB2_ERROR_ALREADY_EXISTS -> MDB2_ERROR_CONSTRAINT
  188.  *
  189.  * + odbc(access):  MS's ODBC driver reports 'no such field' as code
  190.  *   07001, which means 'too few parameters.'  When this option is on
  191.  *   that code gets mapped to MDB2_ERROR_NOSUCHFIELD.
  192.  *
  193.  * @see MDB2_Driver_Common::setOption()
  194.  */
  195.  
  196. define('MDB2_PORTABILITY_ERRORS', 16);
  197.  
  198. /**
  199.  * Portability: convert empty values to null strings in data output by
  200.  * query*() and fetch*().
  201.  * @see MDB2_Driver_Common::setOption()
  202.  */
  203.  
  204. define('MDB2_PORTABILITY_EMPTY_TO_NULL', 32);
  205.  
  206. /**
  207.  * Portability: removes database/table qualifiers from associative indexes
  208.  * @see MDB2_Driver_Common::setOption()
  209.  */
  210.  
  211. define('MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES', 64);
  212.  
  213. /**
  214.  * Portability: turn on all portability features.
  215.  * @see MDB2_Driver_Common::setOption()
  216.  */
  217.  
  218. define('MDB2_PORTABILITY_ALL', 127);
  219.  
  220. /**
  221.  * These are global variables that are used to track the various class instances
  222.  */
  223.  
  224. $GLOBALS['_MDB2_databases'] = array();
  225. $GLOBALS['_MDB2_dsninfo_default'] = array(
  226.     'phptype'  => false,
  227.     'dbsyntax' => false,
  228.     'username' => false,
  229.     'password' => false,
  230.     'protocol' => false,
  231.     'hostspec' => false,
  232.     'port'     => false,
  233.     'socket'   => false,
  234.     'database' => false,
  235.     'mode'     => false,
  236. );
  237.  
  238. /**
  239.  * The main 'MDB2' class is simply a container class with some static
  240.  * methods for creating DB objects as well as some utility functions
  241.  * common to all parts of DB.
  242.  *
  243.  * The object model of MDB2 is as follows (indentation means inheritance):
  244.  *
  245.  * MDB2          The main MDB2 class.  This is simply a utility class
  246.  *              with some 'static' methods for creating MDB2 objects as
  247.  *              well as common utility functions for other MDB2 classes.
  248.  *
  249.  * MDB2_Driver_Common   The base for each MDB2 implementation.  Provides default
  250.  * |            implementations (in OO lingo virtual methods) for
  251.  * |            the actual DB implementations as well as a bunch of
  252.  * |            query utility functions.
  253.  * |
  254.  * +-MDB2_Driver_mysql  The MDB2 implementation for MySQL. Inherits MDB2_Driver_Common.
  255.  *              When calling MDB2::factory or MDB2::connect for MySQL
  256.  *              connections, the object returned is an instance of this
  257.  *              class.
  258.  * +-MDB2_Driver_pgsql  The MDB2 implementation for PostGreSQL. Inherits MDB2_Driver_Common.
  259.  *              When calling MDB2::factory or MDB2::connect for PostGreSQL
  260.  *              connections, the object returned is an instance of this
  261.  *              class.
  262.  *
  263.  * @package  MDB2
  264.  * @category Database
  265.  * @author   Lukas Smith <smith@pooteeweet.org>
  266.  */
  267. class MDB2
  268. {
  269.     // }}}
  270.     // {{{ setOptions()
  271.  
  272.     /**
  273.      * set option array in an exiting database object
  274.      *
  275.      * @param   MDB2_Driver_Common  $db       MDB2 object
  276.      * @param   array   $options  An associative array of option names and their values.
  277.      * @access  public
  278.      */
  279.     function setOptions(&$db, $options)
  280.     {
  281.         if (is_array($options)) {
  282.             foreach ($options as $option => $value) {
  283.                 $test = $db->setOption($option, $value);
  284.                 if (PEAR::isError($test)) {
  285.                     return $test;
  286.                 }
  287.             }
  288.         }
  289.         return MDB2_OK;
  290.     }
  291.  
  292.     // }}}
  293.     // {{{ factory()
  294.  
  295.     /**
  296.      * Create a new MDB2 object for the specified database type
  297.      *
  298.      * IMPORTANT: In order for MDB2 to work properly it is necessary that
  299.      * you make sure that you work with a reference of the original
  300.      * object instead of a copy (this is a PHP4 quirk).
  301.      *
  302.      * For example:
  303.      *     $db =& MDB2::factory($dsn);
  304.      *          ^^
  305.      * And not:
  306.      *     $db = MDB2::factory($dsn);
  307.      *
  308.      * @param   mixed   $dsn      'data source name', see the MDB2::parseDSN
  309.      *                            method for a description of the dsn format.
  310.      *                            Can also be specified as an array of the
  311.      *                            format returned by MDB2::parseDSN.
  312.      * @param   array   $options  An associative array of option names and
  313.      *                            their values.
  314.      * @return  mixed   a newly created MDB2 object, or false on error
  315.      * @access  public
  316.      */
  317.     function &factory($dsn, $options = false)
  318.     {
  319.         $dsninfo = MDB2::parseDSN($dsn);
  320.         if (!array_key_exists('phptype', $dsninfo)) {
  321.             $err =& MDB2::raiseError(MDB2_ERROR_NOT_FOUND,
  322.                 null, null, 'no RDBMS driver specified');
  323.             return $err;
  324.         }
  325.         $class_name = 'MDB2_Driver_'.$dsninfo['phptype'];
  326.  
  327.         if (!class_exists($class_name)) {
  328.             $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
  329.             if (is_array($options) && array_key_exists('debug', $options) && $options['debug']) {
  330.                 $include = include_once($file_name);
  331.             } else {
  332.                 $include = @include_once($file_name);
  333.             }
  334.             if (!$include) {
  335.                 if (!MDB2::fileExists($file_name)) {
  336.                     $msg = "unable to find package '$class_name' file '$file_name'";
  337.                 } else {
  338.                     $msg = "unable to load driver class '$class_name' from file '$file_name'";
  339.                 }
  340.                 $err =& MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, $msg);
  341.                 return $err;
  342.             }
  343.         }
  344.  
  345.         $db =& new $class_name();
  346.         $db->setDSN($dsninfo);
  347.         $err = MDB2::setOptions($db, $options);
  348.         if (PEAR::isError($err)) {
  349.             return $err;
  350.         }
  351.  
  352.         return $db;
  353.     }
  354.  
  355.     // }}}
  356.     // {{{ connect()
  357.  
  358.     /**
  359.      * Create a new MDB2 connection object and connect to the specified
  360.      * database
  361.      *
  362.      * IMPORTANT: In order for MDB2 to work properly it is necessary that
  363.      * you make sure that you work with a reference of the original
  364.      * object instead of a copy (this is a PHP4 quirk).
  365.      *
  366.      * For example:
  367.      *     $db =& MDB2::connect($dsn);
  368.      *          ^^
  369.      * And not:
  370.      *     $db = MDB2::connect($dsn);
  371.      *          ^^
  372.      *
  373.      * @param   mixed   $dsn      'data source name', see the MDB2::parseDSN
  374.      *                            method for a description of the dsn format.
  375.      *                            Can also be specified as an array of the
  376.      *                            format returned by MDB2::parseDSN.
  377.      * @param   array   $options  An associative array of option names and
  378.      *                            their values.
  379.      * @return  mixed   a newly created MDB2 connection object, or a MDB2
  380.      *                  error object on error
  381.      * @access  public
  382.      * @see     MDB2::parseDSN
  383.      */
  384.     function &connect($dsn, $options = false)
  385.     {
  386.         $db =& MDB2::factory($dsn, $options);
  387.         if (PEAR::isError($db)) {
  388.             return $db;
  389.         }
  390.  
  391.         $err = $db->connect();
  392.         if (PEAR::isError($err)) {
  393.             $dsn = $db->getDSN('string', 'xxx');
  394.             $db->disconnect();
  395.             $err->addUserInfo($dsn);
  396.             return $err;
  397.         }
  398.  
  399.         return $db;
  400.     }
  401.  
  402.     // }}}
  403.     // {{{ singleton()
  404.  
  405.     /**
  406.      * Returns a MDB2 connection with the requested DSN.
  407.      * A newnew MDB2 connection object is only created if no object with the
  408.      * reuested DSN exists yet.
  409.      *
  410.      * IMPORTANT: In order for MDB2 to work properly it is necessary that
  411.      * you make sure that you work with a reference of the original
  412.      * object instead of a copy (this is a PHP4 quirk).
  413.      *
  414.      * For example:
  415.      *     $db =& MDB2::singleton($dsn);
  416.      *          ^^
  417.      * And not:
  418.      *     $db = MDB2::singleton($dsn);
  419.      *          ^^
  420.      *
  421.      * @param   mixed   $dsn      'data source name', see the MDB2::parseDSN
  422.      *                            method for a description of the dsn format.
  423.      *                            Can also be specified as an array of the
  424.      *                            format returned by MDB2::parseDSN.
  425.      * @param   array   $options  An associative array of option names and
  426.      *                            their values.
  427.      * @return  mixed   a newly created MDB2 connection object, or a MDB2
  428.      *                  error object on error
  429.      * @access  public
  430.      * @see     MDB2::parseDSN
  431.      */
  432.     function &singleton($dsn = null, $options = false)
  433.     {
  434.         if ($dsn) {
  435.             $dsninfo = MDB2::parseDSN($dsn);
  436.             $dsninfo = array_merge($GLOBALS['_MDB2_dsninfo_default'], $dsninfo);
  437.             $keys = array_keys($GLOBALS['_MDB2_databases']);
  438.             for ($i=0, $j=count($keys); $i<$j; ++$i) {
  439.                 $tmp_dsn = $GLOBALS['_MDB2_databases'][$keys[$i]]->getDSN('array');
  440.                 if (count(array_diff($tmp_dsn, $dsninfo)) == 0) {
  441.                     MDB2::setOptions($GLOBALS['_MDB2_databases'][$keys[$i]], $options);
  442.                     return $GLOBALS['_MDB2_databases'][$keys[$i]];
  443.                 }
  444.             }
  445.         } elseif (is_array($GLOBALS['_MDB2_databases']) && reset($GLOBALS['_MDB2_databases'])) {
  446.             $db =& $GLOBALS['_MDB2_databases'][key($GLOBALS['_MDB2_databases'])];
  447.             return $db;
  448.         }
  449.         $db =& MDB2::factory($dsn, $options);
  450.         return $db;
  451.     }
  452.  
  453.     // }}}
  454.     // {{{ loadFile()
  455.  
  456.     /**
  457.      * load a file (like 'Date')
  458.      *
  459.      * @param  string     $file  name of the file in the MDB2 directory (without '.php')
  460.      * @return string     name of the file that was included
  461.      * @access public
  462.      */
  463.     function loadFile($file)
  464.     {
  465.         $file_name = 'MDB2'.DIRECTORY_SEPARATOR.$file.'.php';
  466.         if (!MDB2::fileExists($file_name)) {
  467.             return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  468.                 'unable to find: '.$file_name);
  469.         }
  470.         if (!include_once($file_name)) {
  471.             return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  472.                 'unable to load driver class: '.$file_name);
  473.         }
  474.         return $file_name;
  475.     }
  476.  
  477.     // }}}
  478.     // {{{ apiVersion()
  479.  
  480.     /**
  481.      * Return the MDB2 API version
  482.      *
  483.      * @return string     the MDB2 API version number
  484.      * @access public
  485.      */
  486.     function apiVersion()
  487.     {
  488.         return '2.0.0';
  489.     }
  490.  
  491.     // }}}
  492.     // {{{ raiseError()
  493.  
  494.     /**
  495.      * This method is used to communicate an error and invoke error
  496.      * callbacks etc.  Basically a wrapper for PEAR::raiseError
  497.      * without the message string.
  498.      *
  499.      * @param mixed    integer error code
  500.      *
  501.      * @param int      error mode, see PEAR_Error docs
  502.      *
  503.      * @param mixed    If error mode is PEAR_ERROR_TRIGGER, this is the
  504.      *                 error level (E_USER_NOTICE etc).  If error mode is
  505.      *                 PEAR_ERROR_CALLBACK, this is the callback function,
  506.      *                 either as a function name, or as an array of an
  507.      *                 object and method name.  For other error modes this
  508.      *                 parameter is ignored.
  509.      *
  510.      * @param string   Extra debug information.  Defaults to the last
  511.      *                 query and native error code.
  512.      *
  513.      * @return object  a PEAR error object
  514.      *
  515.      * @see PEAR_Error
  516.      */
  517.     function &raiseError($code = null, $mode = null, $options = null, $userinfo = null)
  518.     {
  519.         $err =& PEAR::raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true);
  520.         return $err;
  521.     }
  522.  
  523.     // }}}
  524.     // {{{ isError()
  525.  
  526.     /**
  527.      * Tell whether a value is a MDB2 error.
  528.      *
  529.      * @param   mixed $data   the value to test
  530.      * @param   int   $code   if $data is an error object, return true
  531.      *                        only if $code is a string and
  532.      *                        $db->getMessage() == $code or
  533.      *                        $code is an integer and $db->getCode() == $code
  534.      * @access  public
  535.      * @return  bool    true if parameter is an error
  536.      */
  537.     function isError($data, $code = null)
  538.     {
  539.         if (is_a($data, 'MDB2_Error')) {
  540.             if (is_null($code)) {
  541.                 return true;
  542.             } elseif (is_string($code)) {
  543.                 return $data->getMessage() === $code;
  544.             } else {
  545.                 $code = (array)$code;
  546.                 return in_array($data->getCode(), $code);
  547.             }
  548.         }
  549.         return false;
  550.     }
  551.  
  552.     // }}}
  553.     // {{{ isConnection()
  554.     /**
  555.      * Tell whether a value is a MDB2 connection
  556.      *
  557.      * @param mixed $value value to test
  558.      * @return bool whether $value is a MDB2 connection
  559.      * @access public
  560.      */
  561.     function isConnection($value)
  562.     {
  563.         return is_a($value, 'MDB2_Driver_Common');
  564.     }
  565.  
  566.     // }}}
  567.     // {{{ isResult()
  568.     /**
  569.      * Tell whether a value is a MDB2 result
  570.      *
  571.      * @param mixed $value value to test
  572.      * @return bool whether $value is a MDB2 result
  573.      * @access public
  574.      */
  575.     function isResult($value)
  576.     {
  577.         return is_a($value, 'MDB2_Result');
  578.     }
  579.  
  580.     // }}}
  581.     // {{{ isResultCommon()
  582.     /**
  583.      * Tell whether a value is a MDB2 result implementing the common interface
  584.      *
  585.      * @param mixed $value value to test
  586.      * @return bool whether $value is a MDB2 result implementing the common interface
  587.      * @access public
  588.      */
  589.     function isResultCommon($value)
  590.     {
  591.         return is_a($value, 'MDB2_Result_Common');
  592.     }
  593.  
  594.     // }}}
  595.     // {{{ isStatement()
  596.     /**
  597.      * Tell whether a value is a MDB2 statement interface
  598.      *
  599.      * @param mixed $value value to test
  600.      * @return bool whether $value is a MDB2 statement interface
  601.      * @access public
  602.      */
  603.     function isStatement($value)
  604.     {
  605.         return is_a($value, 'MDB2_Statement');
  606.     }
  607.  
  608.     // }}}
  609.     // {{{ errorMessage()
  610.  
  611.     /**
  612.      * Return a textual error message for a MDB2 error code
  613.      *
  614.      * @param   int     $value error code
  615.      * @return  string  error message, or false if the error code was
  616.      *                  not recognized
  617.      * @access public
  618.      */
  619.     function errorMessage($value)
  620.     {
  621.         static $errorMessages;
  622.         if (!isset($errorMessages)) {
  623.             $errorMessages = array(
  624.                 MDB2_ERROR                    => 'unknown error',
  625.                 MDB2_ERROR_ALREADY_EXISTS     => 'already exists',
  626.                 MDB2_ERROR_CANNOT_CREATE      => 'can not create',
  627.                 MDB2_ERROR_CANNOT_ALTER       => 'can not alter',
  628.                 MDB2_ERROR_CANNOT_REPLACE     => 'can not replace',
  629.                 MDB2_ERROR_CANNOT_DELETE      => 'can not delete',
  630.                 MDB2_ERROR_CANNOT_DROP        => 'can not drop',
  631.                 MDB2_ERROR_CONSTRAINT         => 'constraint violation',
  632.                 MDB2_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
  633.                 MDB2_ERROR_DIVZERO            => 'division by zero',
  634.                 MDB2_ERROR_INVALID            => 'invalid',
  635.                 MDB2_ERROR_INVALID_DATE       => 'invalid date or time',
  636.                 MDB2_ERROR_INVALID_NUMBER     => 'invalid number',
  637.                 MDB2_ERROR_MISMATCH           => 'mismatch',
  638.                 MDB2_ERROR_NODBSELECTED       => 'no database selected',
  639.                 MDB2_ERROR_NOSUCHFIELD        => 'no such field',
  640.                 MDB2_ERROR_NOSUCHTABLE        => 'no such table',
  641.                 MDB2_ERROR_NOT_CAPABLE        => 'MDB2 backend not capable',
  642.                 MDB2_ERROR_NOT_FOUND          => 'not found',
  643.                 MDB2_ERROR_NOT_LOCKED         => 'not locked',
  644.                 MDB2_ERROR_SYNTAX             => 'syntax error',
  645.                 MDB2_ERROR_UNSUPPORTED        => 'not supported',
  646.                 MDB2_ERROR_VALUE_COUNT_ON_ROW => 'value count on row',
  647.                 MDB2_ERROR_INVALID_DSN        => 'invalid DSN',
  648.                 MDB2_ERROR_CONNECT_FAILED     => 'connect failed',
  649.                 MDB2_OK                       => 'no error',
  650.                 MDB2_ERROR_NEED_MORE_DATA     => 'insufficient data supplied',
  651.                 MDB2_ERROR_EXTENSION_NOT_FOUND=> 'extension not found',
  652.                 MDB2_ERROR_NOSUCHDB           => 'no such database',
  653.                 MDB2_ERROR_ACCESS_VIOLATION   => 'insufficient permissions',
  654.                 MDB2_ERROR_LOADMODULE         => 'error while including on demand module',
  655.                 MDB2_ERROR_TRUNCATED          => 'truncated',
  656.                 MDB2_ERROR_DEADLOCK           => 'deadlock detected',
  657.             );
  658.         }
  659.  
  660.         if (PEAR::isError($value)) {
  661.             $value = $value->getCode();
  662.         }
  663.  
  664.         return isset($errorMessages[$value]) ?
  665.            $errorMessages[$value] : $errorMessages[MDB2_ERROR];
  666.     }
  667.  
  668.     // }}}
  669.     // {{{ parseDSN()
  670.  
  671.     /**
  672.      * Parse a data source name.
  673.      *
  674.      * Additional keys can be added by appending a URI query string to the
  675.      * end of the DSN.
  676.      *
  677.      * The format of the supplied DSN is in its fullest form:
  678.      * <code>
  679.      *  phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true
  680.      * </code>
  681.      *
  682.      * Most variations are allowed:
  683.      * <code>
  684.      *  phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644
  685.      *  phptype://username:password@hostspec/database_name
  686.      *  phptype://username:password@hostspec
  687.      *  phptype://username@hostspec
  688.      *  phptype://hostspec/database
  689.      *  phptype://hostspec
  690.      *  phptype(dbsyntax)
  691.      *  phptype
  692.      * </code>
  693.      *
  694.      * @param string $dsn Data Source Name to be parsed
  695.      *
  696.      * @return array an associative array with the following keys:
  697.      *  + phptype:  Database backend used in PHP (mysql, odbc etc.)
  698.      *  + dbsyntax: Database used with regards to SQL syntax etc.
  699.      *  + protocol: Communication protocol to use (tcp, unix etc.)
  700.      *  + hostspec: Host specification (hostname[:port])
  701.      *  + database: Database to use on the DBMS server
  702.      *  + username: User name for login
  703.      *  + password: Password for login
  704.      *
  705.      * @author Tomas V.V.Cox <cox@idecnet.com>
  706.      */
  707.     function parseDSN($dsn)
  708.     {
  709.         $parsed = $GLOBALS['_MDB2_dsninfo_default'];
  710.  
  711.         if (is_array($dsn)) {
  712.             $dsn = array_merge($parsed, $dsn);
  713.             if (!$dsn['dbsyntax']) {
  714.                 $dsn['dbsyntax'] = $dsn['phptype'];
  715.             }
  716.             return $dsn;
  717.         }
  718.  
  719.         // Find phptype and dbsyntax
  720.         if (($pos = strpos($dsn, '://')) !== false) {
  721.             $str = substr($dsn, 0, $pos);
  722.             $dsn = substr($dsn, $pos + 3);
  723.         } else {
  724.             $str = $dsn;
  725.             $dsn = null;
  726.         }
  727.  
  728.         // Get phptype and dbsyntax
  729.         // $str => phptype(dbsyntax)
  730.         if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
  731.             $parsed['phptype']  = $arr[1];
  732.             $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2];
  733.         } else {
  734.             $parsed['phptype']  = $str;
  735.             $parsed['dbsyntax'] = $str;
  736.         }
  737.  
  738.         if (!count($dsn)) {
  739.             return $parsed;
  740.         }
  741.  
  742.         // Get (if found): username and password
  743.         // $dsn => username:password@protocol+hostspec/database
  744.         if (($at = strrpos($dsn,'@')) !== false) {
  745.             $str = substr($dsn, 0, $at);
  746.             $dsn = substr($dsn, $at + 1);
  747.             if (($pos = strpos($str, ':')) !== false) {
  748.                 $parsed['username'] = rawurldecode(substr($str, 0, $pos));
  749.                 $parsed['password'] = rawurldecode(substr($str, $pos + 1));
  750.             } else {
  751.                 $parsed['username'] = rawurldecode($str);
  752.             }
  753.         }
  754.  
  755.         // Find protocol and hostspec
  756.  
  757.         // $dsn => proto(proto_opts)/database
  758.         if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) {
  759.             $proto       = $match[1];
  760.             $proto_opts  = $match[2] ? $match[2] : false;
  761.             $dsn         = $match[3];
  762.  
  763.         // $dsn => protocol+hostspec/database (old format)
  764.         } else {
  765.             if (strpos($dsn, '+') !== false) {
  766.                 list($proto, $dsn) = explode('+', $dsn, 2);
  767.             }
  768.             if (strpos($dsn, '/') !== false) {
  769.                 list($proto_opts, $dsn) = explode('/', $dsn, 2);
  770.             } else {
  771.                 $proto_opts = $dsn;
  772.                 $dsn = null;
  773.             }
  774.         }
  775.  
  776.         // process the different protocol options
  777.         $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp';
  778.         $proto_opts = rawurldecode($proto_opts);
  779.         if ($parsed['protocol'] == 'tcp') {
  780.             if (strpos($proto_opts, ':') !== false) {
  781.                 list($parsed['hostspec'], $parsed['port']) = explode(':', $proto_opts);
  782.             } else {
  783.                 $parsed['hostspec'] = $proto_opts;
  784.             }
  785.         } elseif ($parsed['protocol'] == 'unix') {
  786.             $parsed['socket'] = $proto_opts;
  787.         }
  788.  
  789.         // Get dabase if any
  790.         // $dsn => database
  791.         if ($dsn) {
  792.             // /database
  793.             if (($pos = strpos($dsn, '?')) === false) {
  794.                 $parsed['database'] = $dsn;
  795.             // /database?param1=value1¶m2=value2
  796.             } else {
  797.                 $parsed['database'] = substr($dsn, 0, $pos);
  798.                 $dsn = substr($dsn, $pos + 1);
  799.                 if (strpos($dsn, '&') !== false) {
  800.                     $opts = explode('&', $dsn);
  801.                 } else { // database?param1=value1
  802.                     $opts = array($dsn);
  803.                 }
  804.                 foreach ($opts as $opt) {
  805.                     list($key, $value) = explode('=', $opt);
  806.                     if (!isset($parsed[$key])) {
  807.                         // don't allow params overwrite
  808.                         $parsed[$key] = rawurldecode($value);
  809.                     }
  810.                 }
  811.             }
  812.         }
  813.  
  814.         return $parsed;
  815.     }
  816.  
  817.     // }}}
  818.     // {{{ fileExists()
  819.  
  820.     /**
  821.      * checks if a file exists in the include path
  822.      *
  823.      * @access public
  824.      * @param  string   filename
  825.      *
  826.      * @return boolean true success and false on error
  827.      */
  828.     function fileExists($file)
  829.     {
  830.         $dirs = explode(PATH_SEPARATOR, ini_get('include_path'));
  831.         foreach ($dirs as $dir) {
  832.             if (is_readable($dir . DIRECTORY_SEPARATOR . $file)) {
  833.                 return true;
  834.             }
  835.         }
  836.         return false;
  837.     }
  838. }
  839.  
  840. /**
  841.  * MDB2_Error implements a class for reporting portable database error
  842.  * messages.
  843.  *
  844.  * @package MDB2
  845.  * @category Database
  846.  * @author  Stig Bakken <ssb@fast.no>
  847.  */
  848. class MDB2_Error extends PEAR_Error
  849. {
  850.     // }}}
  851.     // {{{ constructor
  852.  
  853.     /**
  854.      * MDB2_Error constructor.
  855.      *
  856.      * @param mixed   $code      MDB2 error code, or string with error message.
  857.      * @param integer $mode      what 'error mode' to operate in
  858.      * @param integer $level     what error level to use for
  859.      *                           $mode & PEAR_ERROR_TRIGGER
  860.      * @param smixed  $debuginfo additional debug info, such as the last query
  861.      */
  862.     function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN,
  863.               $level = E_USER_NOTICE, $debuginfo = null)
  864.     {
  865.         $this->PEAR_Error('MDB2 Error: '.MDB2::errorMessage($code), $code,
  866.             $mode, $level, $debuginfo);
  867.     }
  868. }
  869.  
  870. /**
  871.  * MDB2_Driver_Common: Base class that is extended by each MDB2 driver
  872.  *
  873.  * @package MDB2
  874.  * @category Database
  875.  * @author Lukas Smith <smith@pooteeweet.org>
  876.  */
  877. class MDB2_Driver_Common extends PEAR
  878. {
  879.     /**
  880.      * index of the MDB2 object within the $GLOBALS['_MDB2_databases'] array
  881.      * @var integer
  882.      * @access public
  883.      */
  884.     var $db_index = 0;
  885.  
  886.     /**
  887.      * DSN used for the next query
  888.      * @var array
  889.      * @access protected
  890.      */
  891.     var $dsn = array();
  892.  
  893.     /**
  894.      * DSN that was used to create the current connection
  895.      * @var array
  896.      * @access protected
  897.      */
  898.     var $connected_dsn = array();
  899.  
  900.     /**
  901.      * connection resource
  902.      * @var mixed
  903.      * @access protected
  904.      */
  905.     var $connection = 0;
  906.  
  907.     /**
  908.      * if the current opened connection is a persistent connection
  909.      * @var boolean
  910.      * @access protected
  911.      */
  912.     var $opened_persistent;
  913.  
  914.     /**
  915.      * the name of the database for the next query
  916.      * @var string
  917.      * @access protected
  918.      */
  919.     var $database_name = '';
  920.  
  921.     /**
  922.      * the name of the database currrently selected
  923.      * @var string
  924.      * @access protected
  925.      */
  926.     var $connected_database_name = '';
  927.  
  928.     /**
  929.      * list of all supported features of the given driver
  930.      * @var array
  931.      * @access public
  932.      */
  933.     var $supported = array(
  934.         'sequences' => false,
  935.         'indexes' => false,
  936.         'affected_rows' => false,
  937.         'summary_functions' => false,
  938.         'order_by_text' => false,
  939.         'transactions' => false,
  940.         'current_id' => false,
  941.         'limit_queries' => false,
  942.         'LOBs' => false,
  943.         'replace' => false,
  944.         'sub_selects' => false,
  945.         'auto_increment' => false,
  946.         'primary_key' => false,
  947.     );
  948.  
  949.     /**
  950.      * $options['ssl'] -> determines if ssl should be used for connections
  951.      * $options['field_case'] -> determines what case to force on field/table names
  952.      * $options['disable_query'] -> determines if querys should be executed
  953.      * $options['result_class'] -> class used for result sets
  954.      * $options['buffered_result_class'] -> class used for buffered result sets
  955.      * $options['result_wrap_class'] -> class used to wrap result sets into
  956.      * $options['result_buffering'] -> boolean|integer should results be buffered or not?
  957.      * $options['fetch_class'] -> class to use when fetch mode object is used
  958.      * $options['persistent'] -> boolean persistent connection?
  959.      * $options['debug'] -> integer numeric debug level
  960.      * $options['debug_handler'] -> string function/meothd that captures debug messages
  961.      * $options['lob_buffer_length'] -> integer LOB buffer length
  962.      * $options['log_line_break'] -> string line-break format
  963.      * $options['idxname_format'] -> string pattern for index name
  964.      * $options['seqname_format'] -> string pattern for sequence name
  965.      * $options['seqcol_name'] -> string sequence column name
  966.      * $options['quote_identifier'] -> if identifier quoting should be done when check_option is used
  967.      * $options['use_transactions'] -> boolean
  968.      * $options['decimal_places'] -> integer
  969.      * $options['portability'] -> portability constant
  970.      * $options['modules'] -> short to long module name mapping for __call()
  971.      * @var array
  972.      * @access public
  973.      */
  974.     var $options = array(
  975.             'ssl' => false,
  976.             'field_case' => CASE_LOWER,
  977.             'disable_query' => false,
  978.             'result_class' => 'MDB2_Result_%s',
  979.             'buffered_result_class' => 'MDB2_BufferedResult_%s',
  980.             'result_wrap_class' => false,
  981.             'result_buffering' => true,
  982.             'fetch_class' => 'stdClass',
  983.             'persistent' => false,
  984.             'debug' => 0,
  985.             'debug_handler' => 'MDB2_defaultDebugOutput',
  986.             'lob_buffer_length' => 8192,
  987.             'log_line_break' => "\n",
  988.             'idxname_format' => '%s_idx',
  989.             'seqname_format' => '%s_seq',
  990.             'seqcol_name' => 'sequence',
  991.             'quote_identifier' => false,
  992.             'use_transactions' => false,
  993.             'decimal_places' => 2,
  994.             'portability' => MDB2_PORTABILITY_ALL,
  995.             'modules' => array(
  996.                 'ex' => 'Extended',
  997.                 'dt' => 'Datatype',
  998.                 'mg' => 'Manager',
  999.                 'rv' => 'Reverse',
  1000.                 'na' => 'Native',
  1001.                 'fc' => 'Function',
  1002.             ),
  1003.         );
  1004.  
  1005.     /**
  1006.      * escape character
  1007.      * @var string
  1008.      * @access protected
  1009.      */
  1010.     var $escape_quotes = '';
  1011.  
  1012.     /**
  1013.      * warnings
  1014.      * @var array
  1015.      * @access protected
  1016.      */
  1017.     var $warnings = array();
  1018.  
  1019.     /**
  1020.      * string with the debugging information
  1021.      * @var string
  1022.      * @access public
  1023.      */
  1024.     var $debug_output = '';
  1025.  
  1026.     /**
  1027.      * determine if there is an open transaction
  1028.      * @var boolean
  1029.      * @access protected
  1030.      */
  1031.     var $in_transaction = false;
  1032.  
  1033.     /**
  1034.      * result offset used in the next query
  1035.      * @var integer
  1036.      * @access protected
  1037.      */
  1038.     var $offset = 0;
  1039.  
  1040.     /**
  1041.      * result limit used in the next query
  1042.      * @var integer
  1043.      * @access protected
  1044.      */
  1045.     var $limit = 0;
  1046.  
  1047.     /**
  1048.      * Database backend used in PHP (mysql, odbc etc.)
  1049.      * @var string
  1050.      * @access protected
  1051.      */
  1052.     var $phptype;
  1053.  
  1054.     /**
  1055.      * Database used with regards to SQL syntax etc.
  1056.      * @var string
  1057.      * @access protected
  1058.      */
  1059.     var $dbsyntax;
  1060.  
  1061.     /**
  1062.      * the last query sent to the driver
  1063.      * @var string
  1064.      * @access public
  1065.      */
  1066.     var $last_query;
  1067.  
  1068.     /**
  1069.      * the default fetchmode used
  1070.      * @var integer
  1071.      * @access protected
  1072.      */
  1073.     var $fetchmode = MDB2_FETCHMODE_ORDERED;
  1074.  
  1075.     /**
  1076.      * array of module instances
  1077.      * @var array
  1078.      * @access protected
  1079.      */
  1080.     var $modules = array();
  1081.  
  1082.     /**
  1083.      * determines of the PHP4 destructor emulation has been enabled yet
  1084.     * @var array
  1085.     * @access protected
  1086.     */
  1087.     var $destructor_registered = true;
  1088.  
  1089.     // }}}
  1090.     // {{{ constructor
  1091.  
  1092.     /**
  1093.      * Constructor
  1094.      */
  1095.     function __construct()
  1096.     {
  1097.         end($GLOBALS['_MDB2_databases']);
  1098.         $db_index = key($GLOBALS['_MDB2_databases']) + 1;
  1099.         $GLOBALS['_MDB2_databases'][$db_index] = &$this;
  1100.         $this->db_index = $db_index;
  1101.     }
  1102.  
  1103.     // }}}
  1104.     // {{{ MDB2_Driver_Common
  1105.  
  1106.     /**
  1107.      * PHP 4 Constructor
  1108.      */
  1109.     function MDB2_Driver_Common()
  1110.     {
  1111.         $this->destructor_registered = false;
  1112.         $this->__construct();
  1113.     }
  1114.  
  1115.     // }}}
  1116.     // {{{ Destructor
  1117.  
  1118.     /**
  1119.      *  Destructor
  1120.      */
  1121.     function __destruct()
  1122.     {
  1123.         $this->disconnect(false);
  1124.     }
  1125.  
  1126.     // }}}
  1127.     // {{{ free()
  1128.  
  1129.     /**
  1130.      * Free the internal references so that the instance can be destroyed
  1131.      *
  1132.      * @return boolean true on success, false if result is invalid
  1133.      * @access public
  1134.      */
  1135.     function free()
  1136.     {
  1137.         unset($GLOBALS['_MDB2_databases'][$this->db_index]);
  1138.         unset($this->db_index);
  1139.         return MDB2_OK;
  1140.     }
  1141.  
  1142.     // }}}
  1143.     // {{{ __toString()
  1144.  
  1145.     /**
  1146.      * String conversation
  1147.      *
  1148.      * @return string
  1149.      * @access public
  1150.      */
  1151.     function __toString()
  1152.     {
  1153.         $info = get_class($this);
  1154.         $info.= ': (phptype = '.$this->phptype.', dbsyntax = '.$this->dbsyntax.')';
  1155.         if ($this->connection) {
  1156.             $info.= ' [connected]';
  1157.         }
  1158.         return $info;
  1159.     }
  1160.  
  1161.     // }}}
  1162.     // {{{ errorInfo()
  1163.  
  1164.     /**
  1165.      * This method is used to collect information about an error
  1166.      *
  1167.      * @param integer $error
  1168.      * @return array
  1169.      * @access public
  1170.      */
  1171.     function errorInfo($error = null)
  1172.     {
  1173.         return array($error, null, null);
  1174.     }
  1175.  
  1176.     // }}}
  1177.     // {{{ raiseError()
  1178.  
  1179.     /**
  1180.      * This method is used to communicate an error and invoke error
  1181.      * callbacks etc.  Basically a wrapper for PEAR::raiseError
  1182.      * without the message string.
  1183.      *
  1184.      * @param mixed    integer error code, or a PEAR error object (all
  1185.      *                 other parameters are ignored if this parameter is
  1186.      *                 an object
  1187.      *
  1188.      * @param int      error mode, see PEAR_Error docs
  1189.      *
  1190.      * @param mixed    If error mode is PEAR_ERROR_TRIGGER, this is the
  1191.      *                 error level (E_USER_NOTICE etc).  If error mode is
  1192.      *                 PEAR_ERROR_CALLBACK, this is the callback function,
  1193.      *                 either as a function name, or as an array of an
  1194.      *                 object and method name.  For other error modes this
  1195.      *                 parameter is ignored.
  1196.      *
  1197.      * @param string   Extra debug information.  Defaults to the last
  1198.      *                 query and native error code.
  1199.      *
  1200.      * @return object  a PEAR error object
  1201.      *
  1202.      * @see PEAR_Error
  1203.      */
  1204.     function &raiseError($code = null, $mode = null, $options = null, $userinfo = null)
  1205.     {
  1206.         // The error is yet a MDB2 error object
  1207.         if (PEAR::isError($code)) {
  1208.             // because we use the static PEAR::raiseError, our global
  1209.             // handler should be used if it is set
  1210.             if (is_null($mode) && !empty($this->_default_error_mode)) {
  1211.                 $mode    = $this->_default_error_mode;
  1212.                 $options = $this->_default_error_options;
  1213.             }
  1214.         } elseif (is_null($userinfo) && isset($this->connection)) {
  1215.             if (!empty($this->last_query)) {
  1216.                 $userinfo = "[Last query: {$this->last_query}]\n";
  1217.             }
  1218.             $native_errno = $native_msg = null;
  1219.             list($code, $native_errno, $native_msg) = $this->errorInfo($code);
  1220.             if (!is_null($native_errno)) {
  1221.                 $userinfo.= "[Native code: $native_errno]\n";
  1222.             }
  1223.             if (!is_null($native_msg)) {
  1224.                 $userinfo.= "[Native message: ". strip_tags($native_msg) ."]\n";
  1225.             }
  1226.         } else {
  1227.             $userinfo = "[Error message: $userinfo]\n";
  1228.         }
  1229.  
  1230.         $err =& PEAR::raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true);
  1231.         return $err;
  1232.     }
  1233.  
  1234.     // }}}
  1235.     // {{{ resetWarnings()
  1236.  
  1237.     /**
  1238.      * reset the warning array
  1239.      *
  1240.      * @access public
  1241.      */
  1242.     function resetWarnings()
  1243.     {
  1244.         $this->warnings = array();
  1245.     }
  1246.  
  1247.     // }}}
  1248.     // {{{ getWarnings()
  1249.  
  1250.     /**
  1251.      * get all warnings in reverse order.
  1252.      * This means that the last warning is the first element in the array
  1253.      *
  1254.      * @return array with warnings
  1255.      * @access public
  1256.      * @see resetWarnings()
  1257.      */
  1258.     function getWarnings()
  1259.     {
  1260.         return array_reverse($this->warnings);
  1261.     }
  1262.  
  1263.     // }}}
  1264.     // {{{ setFetchMode()
  1265.  
  1266.     /**
  1267.      * Sets which fetch mode should be used by default on queries
  1268.      * on this connection
  1269.      *
  1270.      * @param integer $fetchmode    MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC
  1271.      *                               or MDB2_FETCHMODE_OBJECT
  1272.      * @param string $object_class  the class name of the object to be returned
  1273.      *                               by the fetch methods when the
  1274.      *                               MDB2_FETCHMODE_OBJECT mode is selected.
  1275.      *                               If no class is specified by default a cast
  1276.      *                               to object from the assoc array row will be
  1277.      *                               done.  There is also the posibility to use
  1278.      *                               and extend the 'MDB2_row' class.
  1279.      *
  1280.      * @return mixed MDB2_OK or MDB2 Error Object
  1281.      * @see MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC, MDB2_FETCHMODE_OBJECT
  1282.      * @access public
  1283.      */
  1284.     function setFetchMode($fetchmode, $object_class = 'stdClass')
  1285.     {
  1286.         switch ($fetchmode) {
  1287.         case MDB2_FETCHMODE_OBJECT:
  1288.             $this->options['fetch_class'] = $object_class;
  1289.         case MDB2_FETCHMODE_ORDERED:
  1290.         case MDB2_FETCHMODE_ASSOC:
  1291.             $this->fetchmode = $fetchmode;
  1292.             break;
  1293.         default:
  1294.             return $this->raiseError('invalid fetchmode mode');
  1295.         }
  1296.  
  1297.         return MDB2_OK;
  1298.     }
  1299.  
  1300.     // }}}
  1301.     // {{{ setOption()
  1302.  
  1303.     /**
  1304.      * set the option for the db class
  1305.      *
  1306.      * @param string $option option name
  1307.      * @param mixed $value value for the option
  1308.      * @return mixed MDB2_OK or MDB2 Error Object
  1309.      * @access public
  1310.      */
  1311.     function setOption($option, $value)
  1312.     {
  1313.         if (array_key_exists($option, $this->options)) {
  1314.             $this->options[$option] = $value;
  1315.             return MDB2_OK;
  1316.         }
  1317.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  1318.             "unknown option $option");
  1319.     }
  1320.  
  1321.     // }}}
  1322.     // {{{ getOption()
  1323.  
  1324.     /**
  1325.      * returns the value of an option
  1326.      *
  1327.      * @param string $option option name
  1328.      * @return mixed the option value or error object
  1329.      * @access public
  1330.      */
  1331.     function getOption($option)
  1332.     {
  1333.         if (array_key_exists($option, $this->options)) {
  1334.             return $this->options[$option];
  1335.         }
  1336.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  1337.             "unknown option $option");
  1338.     }
  1339.  
  1340.     // }}}
  1341.     // {{{ debug()
  1342.  
  1343.     /**
  1344.      * set a debug message
  1345.      *
  1346.      * @param string $message Message with information for the user.
  1347.      * @access public
  1348.      */
  1349.     function debug($message, $scope = '', $is_manip = null)
  1350.     {
  1351.         if ($this->options['debug'] && $this->options['debug_handler']) {
  1352.             call_user_func_array($this->options['debug_handler'], array(&$this, $scope, $message, $is_manip));
  1353.         }
  1354.     }
  1355.  
  1356.     // }}}
  1357.     // {{{ getDebugOutput()
  1358.  
  1359.     /**
  1360.      * output debug info
  1361.      *
  1362.      * @return string content of the debug_output class variable
  1363.      * @access public
  1364.      */
  1365.     function getDebugOutput()
  1366.     {
  1367.         return $this->debug_output;
  1368.     }
  1369.  
  1370.     // }}}
  1371.     // {{{ escape()
  1372.  
  1373.     /**
  1374.      * Quotes a string so it can be safely used in a query. It will quote
  1375.      * the text so it can safely be used within a query.
  1376.      *
  1377.      * @param string $text the input string to quote
  1378.      * @return string quoted string
  1379.      * @access public
  1380.      */
  1381.     function escape($text)
  1382.     {
  1383.         if ($this->escape_quotes !== "'") {
  1384.             $text = str_replace($this->escape_quotes, $this->escape_quotes.$this->escape_quotes, $text);
  1385.         }
  1386.         return str_replace("'", $this->escape_quotes . "'", $text);
  1387.     }
  1388.  
  1389.     // }}}
  1390.     // {{{ quoteIdentifier()
  1391.  
  1392.     /**
  1393.      * Quote a string so it can be safely used as a table or column name
  1394.      *
  1395.      * Delimiting style depends on which database driver is being used.
  1396.      *
  1397.      * NOTE: just because you CAN use delimited identifiers doesn't mean
  1398.      * you SHOULD use them.  In general, they end up causing way more
  1399.      * problems than they solve.
  1400.      *
  1401.      * Portability is broken by using the following characters inside
  1402.      * delimited identifiers:
  1403.      *   + backtick (<kbd>`</kbd>) -- due to MySQL
  1404.      *   + double quote (<kbd>"</kbd>) -- due to Oracle
  1405.      *   + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
  1406.      *
  1407.      * Delimited identifiers are known to generally work correctly under
  1408.      * the following drivers:
  1409.      *   + mssql
  1410.      *   + mysql
  1411.      *   + mysqli
  1412.      *   + oci8
  1413.      *   + odbc(access)
  1414.      *   + odbc(db2)
  1415.      *   + pgsql
  1416.      *   + sqlite
  1417.      *   + sybase
  1418.      *
  1419.      * InterBase doesn't seem to be able to use delimited identifiers
  1420.      * via PHP 4.  They work fine under PHP 5.
  1421.      *
  1422.      * @param string $str  identifier name to be quoted
  1423.      * @param bool   $check_option  check the 'quote_identifier' option
  1424.      *
  1425.      * @return string  quoted identifier string
  1426.      *
  1427.      * @access public
  1428.      */
  1429.     function quoteIdentifier($str, $check_option = false)
  1430.     {
  1431.         if ($check_option && !$this->options['quote_identifier']) {
  1432.             return $str;
  1433.         }
  1434.         return '"' . str_replace('"', '""', $str) . '"';
  1435.     }
  1436.  
  1437.     // }}}
  1438.     // {{{ getConnection()
  1439.  
  1440.     /**
  1441.      * Returns a native connection
  1442.      *
  1443.      * @return  mixed   a valid MDB2 connection object,
  1444.      *                  or a MDB2 error object on error
  1445.      * @access  public
  1446.      */
  1447.     function getConnection()
  1448.     {
  1449.         $result = $this->connect();
  1450.         if (PEAR::isError($result)) {
  1451.             return $result;
  1452.         }
  1453.         return $this->connection;
  1454.     }
  1455.  
  1456.     // }}}
  1457.     // {{{ _fixResultArrayValues()
  1458.  
  1459.     /**
  1460.      * Do all necessary conversions on result arrays to fix DBMS quirks
  1461.      *
  1462.      * @param array  $array  the array to be fixed (passed by reference)
  1463.      * @return void
  1464.      * @access protected
  1465.      */
  1466.     function _fixResultArrayValues(&$array, $mode)
  1467.     {
  1468.         switch ($mode) {
  1469.         case MDB2_PORTABILITY_RTRIM:
  1470.             foreach ($array as $key => $value) {
  1471.                 if (is_string($value)) {
  1472.                     $array[$key] = rtrim($value);
  1473.                 }
  1474.             }
  1475.             break;
  1476.         case MDB2_PORTABILITY_EMPTY_TO_NULL:
  1477.             foreach ($array as $key => $value) {
  1478.                 if ($value === '') {
  1479.                     $array[$key] = null;
  1480.                 }
  1481.             }
  1482.             break;
  1483.         case MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES:
  1484.             $tmp_array = array();
  1485.             foreach ($array as $key => $value) {
  1486.                 $tmp_array[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
  1487.             }
  1488.             $array = $tmp_array;
  1489.             break;
  1490.         case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL):
  1491.             foreach ($array as $key => $value) {
  1492.                 if ($value === '') {
  1493.                     $array[$key] = null;
  1494.                 } elseif (is_string($value)) {
  1495.                     $array[$key] = rtrim($value);
  1496.                 }
  1497.             }
  1498.             break;
  1499.         case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES):
  1500.             $tmp_array = array();
  1501.             foreach ($array as $key => $value) {
  1502.                 if (is_string($value)) {
  1503.                     $value = rtrim($value);
  1504.                 }
  1505.                 $tmp_array[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
  1506.             }
  1507.             $array = $tmp_array;
  1508.             break;
  1509.         case (MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES):
  1510.             $tmp_array = array();
  1511.             foreach ($array as $key => $value) {
  1512.                 if ($value === '') {
  1513.                     $value = null;
  1514.                 }
  1515.                 $tmp_array[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
  1516.             }
  1517.             $array = $tmp_array;
  1518.             break;
  1519.         case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES):
  1520.             $tmp_array = array();
  1521.             foreach ($array as $key => $value) {
  1522.                 if ($value === '') {
  1523.                     $value = null;
  1524.                 } elseif (is_string($value)) {
  1525.                     $value = rtrim($value);
  1526.                 }
  1527.                 $tmp_array[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
  1528.             }
  1529.             $array = $tmp_array;
  1530.             break;
  1531.         }
  1532.     }
  1533.  
  1534.     // }}}
  1535.     // {{{ loadModule()
  1536.  
  1537.     /**
  1538.      * loads a module
  1539.      *
  1540.      * @param string $module name of the module that should be loaded
  1541.      *      (only used for error messages)
  1542.      * @param string $property name of the property into which the class will be loaded
  1543.      * @param boolean $phptype_specific if the class to load for the module
  1544.                                         is specific to the phptype
  1545.      * @return object on success a reference to the given module is returned
  1546.      *                and on failure a PEAR error
  1547.      * @access public
  1548.      */
  1549.     function &loadModule($module, $property = null, $phptype_specific = null)
  1550.     {
  1551.         if (!$property) {
  1552.             $property = strtolower($module);
  1553.         }
  1554.  
  1555.         if (!isset($this->{$property})) {
  1556.             $version = $phptype_specific;
  1557.             if ($phptype_specific !== false) {
  1558.                 $version = true;
  1559.                 $class_name = 'MDB2_Driver_'.$module.'_'.$this->phptype;
  1560.                 $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
  1561.             }
  1562.             if ($phptype_specific === false
  1563.                 || (!class_exists($class_name) && !MDB2::fileExists($file_name))
  1564.             ) {
  1565.                 $version = false;
  1566.                 $class_name = 'MDB2_'.$module;
  1567.                 $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
  1568.             }
  1569.  
  1570.             if (!class_exists($class_name)) {
  1571.                 if ($this->options['debug']) {
  1572.                     $include = include_once($file_name);
  1573.                 } else {
  1574.                     $include = @include_once($file_name);
  1575.                 }
  1576.                 if (!$include) {
  1577.                     if (!MDB2::fileExists($file_name)) {
  1578.                         $msg = "unable to find module '$module' file '$file_name'";
  1579.                     } else {
  1580.                         $msg = "unable to load '$module' driver class from file '$file_name'";
  1581.                     }
  1582.                     $err =& $this->raiseError(MDB2_ERROR_LOADMODULE, null, null, $msg);
  1583.                     return $err;
  1584.                 }
  1585.             }
  1586.  
  1587.             // load modul in a specific version
  1588.             if ($version) {
  1589.                 if (method_exists($class_name, 'getClassName')) {
  1590.                     $class_name_new = call_user_func(array($class_name, 'getClassName'), $this->db_index);
  1591.                     if ($class_name != $class_name_new) {
  1592.                         $class_name = $class_name_new;
  1593.                         $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
  1594.                         if ($this->options['debug']) {
  1595.                             $include = include_once($file_name);
  1596.                         } else {
  1597.                             $include = @include_once($file_name);
  1598.                         }
  1599.                         if (!$include) {
  1600.                             if (!MDB2::fileExists($file_name)) {
  1601.                                 $msg = "unable to find module '$module' file '$file_name'";
  1602.                             } else {
  1603.                                 $msg = "unable to load '$module' driver class from file '$file_name'";
  1604.                             }
  1605.                             $err =& $this->raiseError(MDB2_ERROR_LOADMODULE, null, null, $msg);
  1606.                             return $err;
  1607.                         }
  1608.                     }
  1609.                 }
  1610.             }
  1611.  
  1612.             if (!class_exists($class_name)) {
  1613.                 $err =& $this->raiseError(MDB2_ERROR_LOADMODULE, null, null,
  1614.                     "unable to load module '$module' into property '$property'");
  1615.                 return $err;
  1616.             }
  1617.             $this->{$property} =& new $class_name($this->db_index);
  1618.             $this->modules[$module] =& $this->{$property};
  1619.             if ($version) {
  1620.                 // this wil be used in the connect method to determine if the module
  1621.                 // needs to be loaded with a different version if the server
  1622.                 // version changed in between connects
  1623.                 $this->loaded_version_modules[] = $property;
  1624.             }
  1625.         }
  1626.  
  1627.         return $this->{$property};
  1628.     }
  1629.  
  1630.     /**
  1631.      * Calls a module method using the __call magic method
  1632.      *
  1633.      * @param string Method name.
  1634.      * @param array Arguments.
  1635.      * @return mixed Returned value.
  1636.      */
  1637.     function __call($method, $params)
  1638.     {
  1639.         $module = null;
  1640.         if (preg_match('/^([a-z]+)([A-Z])(.*)$/', $method, $match)
  1641.             && isset($this->options['modules'][$match[1]])
  1642.         ) {
  1643.             $module = $this->options['modules'][$match[1]];
  1644.             $method = strtolower($match[2]).$match[3];
  1645.             if (!isset($this->modules[$module]) || !is_object($this->modules[$module])) {
  1646.                 $result =& $this->loadModule($module);
  1647.                 if (PEAR::isError($result)) {
  1648.                     return $result;
  1649.                 }
  1650.             }
  1651.         } else {
  1652.             foreach ($this->modules as $key => $foo) {
  1653.                 if (is_object($this->modules[$key])
  1654.                     && method_exists($this->modules[$key], $method)
  1655.                 ) {
  1656.                     $module = $key;
  1657.                     break;
  1658.                 }
  1659.             }
  1660.         }
  1661.         if (!is_null($module)) {
  1662.             return call_user_func_array(array(&$this->modules[$module], $method), $params);
  1663.         }
  1664.         trigger_error(sprintf('Call to undefined function: %s::%s().', get_class($this), $method), E_USER_ERROR);
  1665.     }
  1666.  
  1667.     // }}}
  1668.     // {{{ beginTransaction()
  1669.  
  1670.     /**
  1671.      * Start a transaction.
  1672.      *
  1673.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  1674.      * @access public
  1675.      */
  1676.     function beginTransaction()
  1677.     {
  1678.         $this->debug('Starting transaction', 'beginTransaction');
  1679.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  1680.             'beginTransaction: transactions are not supported');
  1681.     }
  1682.  
  1683.     // }}}
  1684.     // {{{ commit()
  1685.  
  1686.     /**
  1687.      * Commit the database changes done during a transaction that is in
  1688.      * progress. This function may only be called when auto-committing is
  1689.      * disabled, otherwise it will fail. Therefore, a new transaction is
  1690.      * implicitly started after committing the pending changes.
  1691.      *
  1692.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  1693.      * @access public
  1694.      */
  1695.     function commit()
  1696.     {
  1697.         $this->debug('commiting transaction', 'commit');
  1698.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  1699.             'commit: commiting transactions is not supported');
  1700.     }
  1701.  
  1702.     // }}}
  1703.     // {{{ rollback()
  1704.  
  1705.     /**
  1706.      * Cancel any database changes done during a transaction that is in
  1707.      * progress. This function may only be called when auto-committing is
  1708.      * disabled, otherwise it will fail. Therefore, a new transaction is
  1709.      * implicitly started after canceling the pending changes.
  1710.      *
  1711.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  1712.      * @access public
  1713.      */
  1714.     function rollback()
  1715.     {
  1716.         $this->debug('rolling back transaction', 'rollback');
  1717.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  1718.             'rollback: rolling back transactions is not supported');
  1719.     }
  1720.  
  1721.     // }}}
  1722.     // {{{ disconnect()
  1723.  
  1724.     /**
  1725.      * Log out and disconnect from the database.
  1726.      *
  1727.      * @return mixed true on success, false if not connected and error
  1728.      *                object on error
  1729.      * @access public
  1730.      */
  1731.     function disconnect()
  1732.     {
  1733.         return MDB2_OK;
  1734.     }
  1735.  
  1736.     // }}}
  1737.     // {{{ setDatabase()
  1738.  
  1739.     /**
  1740.      * Select a different database
  1741.      *
  1742.      * @param string $name name of the database that should be selected
  1743.      * @return string name of the database previously connected to
  1744.      * @access public
  1745.      */
  1746.     function setDatabase($name)
  1747.     {
  1748.         $previous_database_name = (isset($this->database_name)) ? $this->database_name : '';
  1749.         $this->database_name = $name;
  1750.         return $previous_database_name;
  1751.     }
  1752.  
  1753.     // }}}
  1754.     // {{{ getDatabase()
  1755.  
  1756.     /**
  1757.      * get the current database
  1758.      *
  1759.      * @return string name of the database
  1760.      * @access public
  1761.      */
  1762.     function getDatabase()
  1763.     {
  1764.         return $this->database_name;
  1765.     }
  1766.  
  1767.     // }}}
  1768.     // {{{ setDSN()
  1769.  
  1770.     /**
  1771.      * set the DSN
  1772.      *
  1773.      * @param mixed     $dsn    DSN string or array
  1774.      * @return MDB2_OK
  1775.      * @access public
  1776.      */
  1777.     function setDSN($dsn)
  1778.     {
  1779.         $dsn_default = $GLOBALS['_MDB2_dsninfo_default'];
  1780.         $dsn = MDB2::parseDSN($dsn);
  1781.         if (array_key_exists('database', $dsn)) {
  1782.             $this->database_name = $dsn['database'];
  1783.             unset($dsn['database']);
  1784.         }
  1785.         $this->dsn = array_merge($dsn_default, $dsn);
  1786.         return MDB2_OK;
  1787.     }
  1788.  
  1789.     // }}}
  1790.     // {{{ getDSN()
  1791.  
  1792.     /**
  1793.      * return the DSN as a string
  1794.      *
  1795.      * @param string     $type    format to return ("array", "string")
  1796.      * @param string     $hidepw  string to hide the password with
  1797.      * @return mixed DSN in the chosen type
  1798.      * @access public
  1799.      */
  1800.     function getDSN($type = 'string', $hidepw = false)
  1801.     {
  1802.         $dsn = array_merge($GLOBALS['_MDB2_dsninfo_default'], $this->dsn);
  1803.         $dsn['phptype'] = $this->phptype;
  1804.         $dsn['database'] = $this->database_name;
  1805.         if ($hidepw) {
  1806.             $dsn['password'] = $hidepw;
  1807.         }
  1808.         switch ($type) {
  1809.         // expand to include all possible options
  1810.         case 'string':
  1811.            $dsn = $dsn['phptype'].
  1812.                ($dsn['dbsyntax'] ? ('('.$dsn['dbsyntax'].')') : '').
  1813.                '://'.$dsn['username'].':'.
  1814.                 $dsn['password'].'@'.$dsn['hostspec'].
  1815.                 ($dsn['port'] ? (':'.$dsn['port']) : '').
  1816.                 '/'.$dsn['database'];
  1817.             break;
  1818.         case 'array':
  1819.         default:
  1820.             break;
  1821.         }
  1822.         return $dsn;
  1823.     }
  1824.  
  1825.     // }}}
  1826.     // {{{ standaloneQuery()
  1827.  
  1828.    /**
  1829.      * execute a query as database administrator
  1830.      *
  1831.      * @param string $query the SQL query
  1832.      * @param mixed   $types  array that contains the types of the columns in
  1833.      *                        the result set
  1834.      * @param boolean $is_manip  if the query is a manipulation query
  1835.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  1836.      * @access public
  1837.      */
  1838.     function &standaloneQuery($query, $types = null, $is_manip = false)
  1839.     {
  1840.         $offset = $this->offset;
  1841.         $limit = $this->limit;
  1842.         $this->offset = $this->limit = 0;
  1843.         $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
  1844.  
  1845.         $connection = $this->getConnection();
  1846.         if (PEAR::isError($connection)) {
  1847.             return $connection;
  1848.         }
  1849.  
  1850.         $result = $this->_doQuery($query, $is_manip, $connection, false);
  1851.         if (PEAR::isError($result)) {
  1852.             return $result;
  1853.         }
  1854.  
  1855.         if ($is_manip) {
  1856.             $affected_rows =  $this->_affectedRows($connection, $result);
  1857.             return $affected_rows;
  1858.         }
  1859.         $result =& $this->_wrapResult($result, $types, true, false, $limit, $offset);
  1860.         return $result;
  1861.     }
  1862.  
  1863.     // }}}
  1864.     // {{{ _modifyQuery()
  1865.  
  1866.     /**
  1867.      * Changes a query string for various DBMS specific reasons
  1868.      *
  1869.      * @param string $query  query to modify
  1870.      * @return the new (modified) query
  1871.      * @access protected
  1872.      */
  1873.     function _modifyQuery($query)
  1874.     {
  1875.         return $query;
  1876.     }
  1877.  
  1878.     // }}}
  1879.     // {{{ _doQuery()
  1880.  
  1881.     /**
  1882.      * Execute a query
  1883.      * @param string $query  query
  1884.      * @param boolean $is_manip  if the query is a manipulation query
  1885.      * @param resource $connection
  1886.      * @param string $database_name
  1887.      * @return result or error object
  1888.      * @access protected
  1889.      */
  1890.     function _doQuery($query, $is_manip = false, $connection = null, $database_name = null)
  1891.     {
  1892.         $this->last_query = $query;
  1893.         $this->debug($query, 'query', $is_manip);
  1894.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  1895.             '_doQuery: method not implemented');
  1896.     }
  1897.  
  1898.     // }}}
  1899.     // {{{ _affectedRows()
  1900.  
  1901.     /**
  1902.      * returns the number of rows affected
  1903.      *
  1904.      * @param resource $result
  1905.      * @param resource $connection
  1906.      * @return mixed MDB2 Error Object or the number of rows affected
  1907.      * @access private
  1908.      */
  1909.     function _affectedRows($connection, $result = null)
  1910.     {
  1911.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  1912.             '_affectedRows: method not implemented');
  1913.     }
  1914.  
  1915.     // }}}
  1916.     // {{{ exec()
  1917.  
  1918.     /**
  1919.      * Execute a manipulation query to the database and return any the affected rows
  1920.      *
  1921.      * @param string $query the SQL query
  1922.      * @return mixed affected rows on success, a MDB2 error on failure
  1923.      * @access public
  1924.      */
  1925.     function exec($query)
  1926.     {
  1927.         $offset = $this->offset;
  1928.         $limit = $this->limit;
  1929.         $this->offset = $this->limit = 0;
  1930.         $query = $this->_modifyQuery($query, true, $limit, $offset);
  1931.  
  1932.         $connection = $this->getConnection();
  1933.         if (PEAR::isError($connection)) {
  1934.             return $connection;
  1935.         }
  1936.  
  1937.         $result = $this->_doQuery($query, true, $connection, $this->database_name);
  1938.         if (PEAR::isError($result)) {
  1939.             return $result;
  1940.         }
  1941.  
  1942.         return $this->_affectedRows($connection, $result);
  1943.     }
  1944.  
  1945.     // }}}
  1946.     // {{{ query()
  1947.  
  1948.     /**
  1949.      * Send a query to the database and return any results
  1950.      *
  1951.      * @param string $query the SQL query
  1952.      * @param mixed   $types  array that contains the types of the columns in
  1953.      *                        the result set
  1954.      * @param mixed $result_class string which specifies which result class to use
  1955.      * @param mixed $result_wrap_class string which specifies which class to wrap results in
  1956.      * @return object a result handle on success, a MDB2 error on failure
  1957.      * @access public
  1958.      */
  1959.     function &query($query, $types = null, $result_class = true, $result_wrap_class = false)
  1960.     {
  1961.         $offset = $this->offset;
  1962.         $limit = $this->limit;
  1963.         $this->offset = $this->limit = 0;
  1964.         $query = $this->_modifyQuery($query, false, $limit, $offset);
  1965.  
  1966.         $connection = $this->getConnection();
  1967.         if (PEAR::isError($connection)) {
  1968.             return $connection;
  1969.         }
  1970.  
  1971.         $result = $this->_doQuery($query, false, $connection, $this->database_name);
  1972.         if (PEAR::isError($result)) {
  1973.             return $result;
  1974.         }
  1975.  
  1976.         $result =& $this->_wrapResult($result, $types, $result_class, $result_wrap_class, $limit, $offset);
  1977.         return $result;
  1978.     }
  1979.  
  1980.     // }}}
  1981.     // {{{ _wrapResult()
  1982.  
  1983.     /**
  1984.      * wrap a result set into the correct class
  1985.      *
  1986.      * @param ressource $result
  1987.      * @param mixed   $types  array that contains the types of the columns in
  1988.      *                        the result set
  1989.      * @param mixed $result_class string which specifies which result class to use
  1990.      * @param mixed $result_wrap_class string which specifies which class to wrap results in
  1991.      * @param string $limit number of rows to select
  1992.      * @param string $offset first row to select
  1993.      * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
  1994.      * @access protected
  1995.      */
  1996.     function &_wrapResult($result, $types = array(), $result_class = true,
  1997.         $result_wrap_class = false, $limit = null, $offset = null)
  1998.     {
  1999.         if ($types === true) {
  2000.             $this->loadModule('Reverse', null, true);
  2001.             $tableInfo = $this->reverse->tableInfo($result);
  2002.             if (PEAR::isError($tableInfo)) {
  2003.                 return $tableInfo;
  2004.             }
  2005.             $types = array();
  2006.             foreach ($tableInfo as $field) {
  2007.                 $types[] = $field['mdb2type'];
  2008.             }
  2009.         }
  2010.  
  2011.         if ($result_class === true) {
  2012.             $result_class = $this->options['result_buffering']
  2013.                 ? $this->options['buffered_result_class'] : $this->options['result_class'];
  2014.         }
  2015.  
  2016.         if ($result_class) {
  2017.             $class_name = sprintf($result_class, $this->phptype);
  2018.             if (!class_exists($class_name)) {
  2019.                 $err =& $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  2020.                     '_wrapResult: result class does not exist '.$class_name);
  2021.                 return $err;
  2022.             }
  2023.             $result =& new $class_name($this, $result, $limit, $offset);
  2024.             if (!MDB2::isResultCommon($result)) {
  2025.                 $err =& $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  2026.                     '_wrapResult: result class is not extended from MDB2_Result_Common');
  2027.                 return $err;
  2028.             }
  2029.             if (!empty($types)) {
  2030.                 $err = $result->setResultTypes($types);
  2031.                 if (PEAR::isError($err)) {
  2032.                     $result->free();
  2033.                     return $err;
  2034.                 }
  2035.             }
  2036.         }
  2037.         if ($result_wrap_class === true) {
  2038.             $result_wrap_class = $this->options['result_wrap_class'];
  2039.         }
  2040.         if ($result_wrap_class) {
  2041.             if (!class_exists($result_wrap_class)) {
  2042.                 $err =& $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  2043.                     '_wrapResult: result wrap class does not exist '.$result_wrap_class);
  2044.                 return $err;
  2045.             }
  2046.             $result =& new $result_wrap_class($result, $this->fetchmode);
  2047.         }
  2048.         return $result;
  2049.     }
  2050.  
  2051.     // }}}
  2052.     // {{{ getServerVersion()
  2053.  
  2054.     /**
  2055.      * return version information about the server
  2056.      *
  2057.      * @param string     $native  determines if the raw version string should be returned
  2058.      * @return mixed array with versoin information or row string
  2059.      * @access public
  2060.      */
  2061.     function getServerVersion($native = false)
  2062.     {
  2063.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2064.             'getServerVersion: method not implemented');
  2065.     }
  2066.  
  2067.     // }}}
  2068.     // {{{ setLimit()
  2069.  
  2070.     /**
  2071.      * set the range of the next query
  2072.      *
  2073.      * @param string $limit number of rows to select
  2074.      * @param string $offset first row to select
  2075.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  2076.      * @access public
  2077.      */
  2078.     function setLimit($limit, $offset = null)
  2079.     {
  2080.         if (!$this->supports('limit_queries')) {
  2081.             return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2082.                 'setLimit: limit is not supported by this driver');
  2083.         }
  2084.         $limit = (int)$limit;
  2085.         if ($limit < 0) {
  2086.             return $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
  2087.                 'setLimit: it was not specified a valid selected range row limit');
  2088.         }
  2089.         $this->limit = $limit;
  2090.         if (!is_null($offset)) {
  2091.             $offset = (int)$offset;
  2092.             if ($offset < 0) {
  2093.                 return $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
  2094.                     'setLimit: it was not specified a valid first selected range row');
  2095.             }
  2096.             $this->offset = $offset;
  2097.         }
  2098.         return MDB2_OK;
  2099.     }
  2100.  
  2101.     // }}}
  2102.     // {{{ subSelect()
  2103.  
  2104.     /**
  2105.      * simple subselect emulation: leaves the query untouched for all RDBMS
  2106.      * that support subselects
  2107.      *
  2108.      * @access public
  2109.      *
  2110.      * @param string $query the SQL query for the subselect that may only
  2111.      *                      return a column
  2112.      * @param string $type determines type of the field
  2113.      *
  2114.      * @return string the query
  2115.      */
  2116.     function subSelect($query, $type = false)
  2117.     {
  2118.         if ($this->supports('sub_selects') === true) {
  2119.             return $query;
  2120.         }
  2121.  
  2122.         if (!$this->supports('sub_selects')) {
  2123.             return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2124.                 'subSelect: method not implemented');
  2125.         }
  2126.  
  2127.         $col = $this->queryCol($query, $type);
  2128.         if (PEAR::isError($col)) {
  2129.             return $col;
  2130.         }
  2131.         if (!is_array($col) || count($col) == 0) {
  2132.             return 'NULL';
  2133.         }
  2134.         if ($type) {
  2135.             $this->loadModule('Datatype', null, true);
  2136.             return $this->datatype->implodeArray($col, $type);
  2137.         }
  2138.         return implode(', ', $col);
  2139.     }
  2140.  
  2141.     // }}}
  2142.     // {{{ replace()
  2143.  
  2144.     /**
  2145.      * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
  2146.      * query, except that if there is already a row in the table with the same
  2147.      * key field values, the REPLACE query just updates its values instead of
  2148.      * inserting a new row.
  2149.      *
  2150.      * The REPLACE type of query does not make part of the SQL standards. Since
  2151.      * pratically only MySQL implements it natively, this type of query is
  2152.      * emulated through this method for other DBMS using standard types of
  2153.      * queries inside a transaction to assure the atomicity of the operation.
  2154.      *
  2155.      * @param string $table name of the table on which the REPLACE query will
  2156.      *       be executed.
  2157.      * @param array $fields associative array that describes the fields and the
  2158.      *       values that will be inserted or updated in the specified table. The
  2159.      *       indexes of the array are the names of all the fields of the table.
  2160.      *       The values of the array are also associative arrays that describe
  2161.      *       the values and other properties of the table fields.
  2162.      *
  2163.      *       Here follows a list of field properties that need to be specified:
  2164.      *
  2165.      *       value
  2166.      *           Value to be assigned to the specified field. This value may be
  2167.      *           of specified in database independent type format as this
  2168.      *           function can perform the necessary datatype conversions.
  2169.      *
  2170.      *           Default: this property is required unless the Null property is
  2171.      *           set to 1.
  2172.      *
  2173.      *       type
  2174.      *           Name of the type of the field. Currently, all types Metabase
  2175.      *           are supported except for clob and blob.
  2176.      *
  2177.      *           Default: no type conversion
  2178.      *
  2179.      *       null
  2180.      *           Boolean property that indicates that the value for this field
  2181.      *           should be set to null.
  2182.      *
  2183.      *           The default value for fields missing in INSERT queries may be
  2184.      *           specified the definition of a table. Often, the default value
  2185.      *           is already null, but since the REPLACE may be emulated using
  2186.      *           an UPDATE query, make sure that all fields of the table are
  2187.      *           listed in this function argument array.
  2188.      *
  2189.      *           Default: 0
  2190.      *
  2191.      *       key
  2192.      *           Boolean property that indicates that this field should be
  2193.      *           handled as a primary key or at least as part of the compound
  2194.      *           unique index of the table that will determine the row that will
  2195.      *           updated if it exists or inserted a new row otherwise.
  2196.      *
  2197.      *           This function will fail if no key field is specified or if the
  2198.      *           value of a key field is set to null because fields that are
  2199.      *           part of unique index they may not be null.
  2200.      *
  2201.      *           Default: 0
  2202.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  2203.      * @access public
  2204.      */
  2205.     function replace($table, $fields)
  2206.     {
  2207.         if (!$this->supports('replace')) {
  2208.             return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2209.                 'replace: replace query is not supported');
  2210.         }
  2211.         $count = count($fields);
  2212.         $condition = $values = array();
  2213.         for ($colnum = 0, reset($fields); $colnum < $count; next($fields), $colnum++) {
  2214.             $name = key($fields);
  2215.             if (isset($fields[$name]['null']) && $fields[$name]['null']) {
  2216.                 $value = 'NULL';
  2217.             } else {
  2218.                 if (isset($fields[$name]['type'])) {
  2219.                     $value = $this->quote($fields[$name]['value'], $fields[$name]['type']);
  2220.                 } else {
  2221.                     $value = $fields[$name]['value'];
  2222.                 }
  2223.             }
  2224.             $values[$name] = $value;
  2225.             if (isset($fields[$name]['key']) && $fields[$name]['key']) {
  2226.                 if ($value === 'NULL') {
  2227.                     return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
  2228.                         'replace: key value '.$name.' may not be NULL');
  2229.                 }
  2230.                 $condition[] = $name . '=' . $value;
  2231.             }
  2232.         }
  2233.         if (empty($condition)) {
  2234.             return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
  2235.                 'replace: not specified which fields are keys');
  2236.         }
  2237.  
  2238.         $result = null;
  2239.         $in_transaction = $this->in_transaction;
  2240.         if (!$in_transaction && PEAR::isError($result = $this->beginTransaction())) {
  2241.             return $result;
  2242.         }
  2243.  
  2244.         $connection = $this->getConnection();
  2245.         if (PEAR::isError($connection)) {
  2246.             return $connection;
  2247.         }
  2248.  
  2249.         $condition = ' WHERE '.implode(' AND ', $condition);
  2250.         $query = "DELETE FROM $table$condition";
  2251.         $result = $this->_doQuery($query, true, $connection);
  2252.         if (!PEAR::isError($result)) {
  2253.             $affected_rows = $this->_affectedRows($connection, $result);
  2254.             $insert = implode(', ', array_keys($values));
  2255.             $values = implode(', ', $values);
  2256.             $query = "INSERT INTO $table ($insert) VALUES ($values)";
  2257.             $result = $this->_doQuery($query, true, $connection);
  2258.             if (!PEAR::isError($result)) {
  2259.                 $affected_rows += $this->_affectedRows($connection, $result);;
  2260.             }
  2261.         }
  2262.  
  2263.         if (!$in_transaction) {
  2264.             if (PEAR::isError($result)) {
  2265.                 $this->rollback();
  2266.             } else {
  2267.                 $result = $this->commit();
  2268.             }
  2269.         }
  2270.  
  2271.         if (PEAR::isError($result)) {
  2272.             return $result;
  2273.         }
  2274.  
  2275.         return $affected_rows;
  2276.     }
  2277.  
  2278.     // }}}
  2279.     // {{{ prepare()
  2280.  
  2281.     /**
  2282.      * Prepares a query for multiple execution with execute().
  2283.      * With some database backends, this is emulated.
  2284.      * prepare() requires a generic query as string like
  2285.      * 'INSERT INTO numbers VALUES(?,?)' or
  2286.      * 'INSERT INTO numbers VALUES(:foo,:bar)'.
  2287.      * The ? and :[a-zA-Z] and  are placeholders which can be set using
  2288.      * bindParam() and the query can be send off using the execute() method.
  2289.      *
  2290.      * @param string $query the query to prepare
  2291.      * @param mixed   $types  array that contains the types of the placeholders
  2292.      * @param mixed   $result_types  array that contains the types of the columns in
  2293.      *                        the result set, if set to MDB2_PREPARE_MANIP the
  2294.                               query is handled as a manipulation query
  2295.      * @param mixed   $lobs   key (field) value (parameter) pair for all lob placeholders
  2296.      * @return mixed resource handle for the prepared query on success, a MDB2
  2297.      *        error on failure
  2298.      * @access public
  2299.      * @see bindParam, execute
  2300.      */
  2301.     function &prepare($query, $types = null, $result_types = null, $lobs = array())
  2302.     {
  2303.         $is_manip = ($result_types === MDB2_PREPARE_MANIP);
  2304.         $offset = $this->offset;
  2305.         $limit = $this->limit;
  2306.         $this->offset = $this->limit = 0;
  2307.         $this->debug($query, 'prepare', $is_manip);
  2308.         $positions = array();
  2309.         $placeholder_type_guess = $placeholder_type = null;
  2310.         $question = '?';
  2311.         $colon = ':';
  2312.         $positions = array();
  2313.         $position = 0;
  2314.         while ($position < strlen($query)) {
  2315.             $q_position = strpos($query, $question, $position);
  2316.             $c_position = strpos($query, $colon, $position);
  2317.             if ($q_position && $c_position) {
  2318.                 $p_position = min($q_position, $c_position);
  2319.             } elseif ($q_position) {
  2320.                 $p_position = $q_position;
  2321.             } elseif ($c_position) {
  2322.                 $p_position = $c_position;
  2323.             } else {
  2324.                 break;
  2325.             }
  2326.             if (is_null($placeholder_type)) {
  2327.                 $placeholder_type_guess = $query[$p_position];
  2328.             }
  2329.             if (is_int($quote = strpos($query, "'", $position)) && $quote < $p_position) {
  2330.                 if (!is_int($end_quote = strpos($query, "'", $quote + 1))) {
  2331.                     $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
  2332.                         'prepare: query with an unterminated text string specified');
  2333.                     return $err;
  2334.                 }
  2335.                 switch ($this->escape_quotes) {
  2336.                 case '':
  2337.                 case "'":
  2338.                     $position = $end_quote + 1;
  2339.                     break;
  2340.                 default:
  2341.                     if ($end_quote == $quote + 1) {
  2342.                         $position = $end_quote + 1;
  2343.                     } else {
  2344.                         if ($query[$end_quote-1] == $this->escape_quotes) {
  2345.                             $position = $end_quote;
  2346.                         } else {
  2347.                             $position = $end_quote + 1;
  2348.                         }
  2349.                     }
  2350.                     break;
  2351.                 }
  2352.             } elseif ($query[$position] == $placeholder_type_guess) {
  2353.                 if (is_null($placeholder_type)) {
  2354.                     $placeholder_type = $query[$p_position];
  2355.                     $question = $colon = $placeholder_type;
  2356.                     if (is_array($types) && !empty($types)) {
  2357.                         if ($placeholder_type == ':') {
  2358.                             if (is_int(key($types))) {
  2359.                                 $types_tmp = $types;
  2360.                                 $types = array();
  2361.                                 $count = -1;
  2362.                             }
  2363.                         } else {
  2364.                             $types = array_values($types);
  2365.                         }
  2366.                     }
  2367.                 }
  2368.                 if ($placeholder_type == ':') {
  2369.                     $parameter = preg_replace('/^.{'.($position+1).'}([a-z0-9_]+).*$/si', '\\1', $query);
  2370.                     if ($parameter === '') {
  2371.                         $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
  2372.                             'prepare: named parameter with an empty name');
  2373.                         return $err;
  2374.                     }
  2375.                     $positions[$parameter] = $p_position;
  2376.                     $query = substr_replace($query, '?', $position, strlen($parameter)+1);
  2377.                     // use parameter name in type array
  2378.                     if (isset($count) && isset($types_tmp[++$count])) {
  2379.                         $types[$parameter] = $types_tmp[$count];
  2380.                     }
  2381.                 } else {
  2382.                     $positions[] = $p_position;
  2383.                 }
  2384.                 $position = $p_position + 1;
  2385.             } else {
  2386.                 $position = $p_position;
  2387.             }
  2388.         }
  2389.         $class_name = 'MDB2_Statement_'.$this->phptype;
  2390.         $statement = null;
  2391.         $obj =& new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
  2392.         return $obj;
  2393.     }
  2394.  
  2395.     // }}}
  2396.     // {{{ quote()
  2397.  
  2398.     /**
  2399.      * Convert a text value into a DBMS specific format that is suitable to
  2400.      * compose query statements.
  2401.      *
  2402.      * @param string $value text string value that is intended to be converted.
  2403.      * @param string $type type to which the value should be converted to
  2404.      * @return string text string that represents the given argument value in
  2405.      *       a DBMS specific format.
  2406.      * @access public
  2407.      */
  2408.     function quote($value, $type = null, $quote = true)
  2409.     {
  2410.         $result = $this->loadModule('Datatype', null, true);
  2411.         if (PEAR::isError($result)) {
  2412.             return $result;
  2413.         }
  2414.         return $this->datatype->quote($value, $type, $quote);
  2415.     }
  2416.  
  2417.     // }}}
  2418.     // {{{ getDeclaration()
  2419.  
  2420.     /**
  2421.      * Obtain DBMS specific SQL code portion needed to declare
  2422.      * of the given type
  2423.      *
  2424.      * @param string $type type to which the value should be converted to
  2425.      * @param string  $name   name the field to be declared.
  2426.      * @param string  $field  definition of the field
  2427.      * @return string  DBMS specific SQL code portion that should be used to
  2428.      *                 declare the specified field.
  2429.      * @access public
  2430.      */
  2431.     function getDeclaration($type, $name, $field)
  2432.     {
  2433.         $result = $this->loadModule('Datatype', null, true);
  2434.         if (PEAR::isError($result)) {
  2435.             return $result;
  2436.         }
  2437.         return $this->datatype->getDeclaration($type, $name, $field);
  2438.     }
  2439.  
  2440.     // }}}
  2441.     // {{{ compareDefinition()
  2442.  
  2443.     /**
  2444.      * Obtain an array of changes that may need to applied
  2445.      *
  2446.      * @param array $current new definition
  2447.      * @param array  $previous old definition
  2448.      * @return array  containg all changes that will need to be applied
  2449.      * @access public
  2450.      */
  2451.     function compareDefinition($current, $previous)
  2452.     {
  2453.         $result = $this->loadModule('Datatype', null, true);
  2454.         if (PEAR::isError($result)) {
  2455.             return $result;
  2456.         }
  2457.         return $this->datatype->compareDefinition($current, $previous);
  2458.     }
  2459.  
  2460.     // }}}
  2461.     // {{{ supports()
  2462.  
  2463.     /**
  2464.      * Tell whether a DB implementation or its backend extension
  2465.      * supports a given feature.
  2466.      *
  2467.      * @param string $feature name of the feature (see the MDB2 class doc)
  2468.      * @return boolean|string   whether this DB implementation supports $feature
  2469.      *                          false means no, true means native, 'emulated' means emulated
  2470.      * @access public
  2471.      */
  2472.     function supports($feature)
  2473.     {
  2474.         if (array_key_exists($feature, $this->supported)) {
  2475.             return $this->supported[$feature];
  2476.         }
  2477.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2478.             "unknown support feature $feature");
  2479.     }
  2480.  
  2481.     // }}}
  2482.     // {{{ getSequenceName()
  2483.  
  2484.     /**
  2485.      * adds sequence name formatting to a sequence name
  2486.      *
  2487.      * @param string $sqn name of the sequence
  2488.      * @return string formatted sequence name
  2489.      * @access public
  2490.      */
  2491.     function getSequenceName($sqn)
  2492.     {
  2493.         return sprintf($this->options['seqname_format'],
  2494.             preg_replace('/[^a-z0-9_]/i', '_', $sqn));
  2495.     }
  2496.  
  2497.     // }}}
  2498.     // {{{ getIndexName()
  2499.  
  2500.     /**
  2501.      * adds index name formatting to a index name
  2502.      *
  2503.      * @param string $idx name of the index
  2504.      * @return string formatted index name
  2505.      * @access public
  2506.      */
  2507.     function getIndexName($idx)
  2508.     {
  2509.         return sprintf($this->options['idxname_format'],
  2510.             preg_replace('/[^a-z0-9_]/i', '_', $idx));
  2511.     }
  2512.  
  2513.     // }}}
  2514.     // {{{ nextID()
  2515.  
  2516.     /**
  2517.      * returns the next free id of a sequence
  2518.      *
  2519.      * @param string $seq_name name of the sequence
  2520.      * @param boolean $ondemand when true the seqence is
  2521.      *                           automatic created, if it
  2522.      *                           not exists
  2523.      * @return mixed MDB2 Error Object or id
  2524.      * @access public
  2525.      */
  2526.     function nextID($seq_name, $ondemand = true)
  2527.     {
  2528.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2529.             'nextID: method not implemented');
  2530.     }
  2531.  
  2532.     // }}}
  2533.     // {{{ lastInsertID()
  2534.  
  2535.     /**
  2536.      * returns the autoincrement ID if supported or $id
  2537.      *
  2538.      * @param mixed $id value as returned by getBeforeId()
  2539.      * @param string $table name of the table into which a new row was inserted
  2540.      * @return mixed MDB2 Error Object or id
  2541.      * @access public
  2542.      */
  2543.     function lastInsertID($table = null, $field = null)
  2544.     {
  2545.         $seq = $table.(empty($field) ? '' : '_'.$field);
  2546.         return $this->currID($seq);
  2547.     }
  2548.  
  2549.     // }}}
  2550.     // {{{ currID()
  2551.  
  2552.     /**
  2553.      * returns the current id of a sequence
  2554.      *
  2555.      * @param string $seq_name name of the sequence
  2556.      * @return mixed MDB2 Error Object or id
  2557.      * @access public
  2558.      */
  2559.     function currID($seq_name)
  2560.     {
  2561.         $this->warnings[] = 'database does not support getting current
  2562.             sequence value, the sequence value was incremented';
  2563.         return $this->nextID($seq_name);
  2564.     }
  2565.  
  2566.     // }}}
  2567.     // {{{ queryOne()
  2568.  
  2569.     /**
  2570.      * Execute the specified query, fetch the value from the first column of
  2571.      * the first row of the result set and then frees
  2572.      * the result set.
  2573.      *
  2574.      * @param string $query the SELECT query statement to be executed.
  2575.      * @param string $type optional argument that specifies the expected
  2576.      *       datatype of the result set field, so that an eventual conversion
  2577.      *       may be performed. The default datatype is text, meaning that no
  2578.      *       conversion is performed
  2579.      * @return mixed MDB2_OK or field value on success, a MDB2 error on failure
  2580.      * @access public
  2581.      */
  2582.     function queryOne($query, $type = null)
  2583.     {
  2584.         $result = $this->query($query, $type);
  2585.         if (!MDB2::isResultCommon($result)) {
  2586.             return $result;
  2587.         }
  2588.  
  2589.         $one = $result->fetchOne();
  2590.         $result->free();
  2591.         return $one;
  2592.     }
  2593.  
  2594.     // }}}
  2595.     // {{{ queryRow()
  2596.  
  2597.     /**
  2598.      * Execute the specified query, fetch the values from the first
  2599.      * row of the result set into an array and then frees
  2600.      * the result set.
  2601.      *
  2602.      * @param string $query the SELECT query statement to be executed.
  2603.      * @param array $types optional array argument that specifies a list of
  2604.      *       expected datatypes of the result set columns, so that the eventual
  2605.      *       conversions may be performed. The default list of datatypes is
  2606.      *       empty, meaning that no conversion is performed.
  2607.      * @param int $fetchmode how the array data should be indexed
  2608.      * @return mixed MDB2_OK or data array on success, a MDB2 error on failure
  2609.      * @access public
  2610.      */
  2611.     function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT)
  2612.     {
  2613.         $result = $this->query($query, $types);
  2614.         if (!MDB2::isResultCommon($result)) {
  2615.             return $result;
  2616.         }
  2617.  
  2618.         $row = $result->fetchRow($fetchmode);
  2619.         $result->free();
  2620.         return $row;
  2621.     }
  2622.  
  2623.     // }}}
  2624.     // {{{ queryCol()
  2625.  
  2626.     /**
  2627.      * Execute the specified query, fetch the value from the first column of
  2628.      * each row of the result set into an array and then frees the result set.
  2629.      *
  2630.      * @param string $query the SELECT query statement to be executed.
  2631.      * @param string $type optional argument that specifies the expected
  2632.      *       datatype of the result set field, so that an eventual conversion
  2633.      *       may be performed. The default datatype is text, meaning that no
  2634.      *       conversion is performed
  2635.      * @param int $colnum the row number to fetch
  2636.      * @return mixed MDB2_OK or data array on success, a MDB2 error on failure
  2637.      * @access public
  2638.      */
  2639.     function queryCol($query, $type = null, $colnum = 0)
  2640.     {
  2641.         $result = $this->query($query, $type);
  2642.         if (!MDB2::isResultCommon($result)) {
  2643.             return $result;
  2644.         }
  2645.  
  2646.         $col = $result->fetchCol($colnum);
  2647.         $result->free();
  2648.         return $col;
  2649.     }
  2650.  
  2651.     // }}}
  2652.     // {{{ queryAll()
  2653.  
  2654.     /**
  2655.      * Execute the specified query, fetch all the rows of the result set into
  2656.      * a two dimensional array and then frees the result set.
  2657.      *
  2658.      * @param string $query the SELECT query statement to be executed.
  2659.      * @param array $types optional array argument that specifies a list of
  2660.      *       expected datatypes of the result set columns, so that the eventual
  2661.      *       conversions may be performed. The default list of datatypes is
  2662.      *       empty, meaning that no conversion is performed.
  2663.      * @param int $fetchmode how the array data should be indexed
  2664.      * @param boolean $rekey if set to true, the $all will have the first
  2665.      *       column as its first dimension
  2666.      * @param boolean $force_array used only when the query returns exactly
  2667.      *       two columns. If true, the values of the returned array will be
  2668.      *       one-element arrays instead of scalars.
  2669.      * @param boolean $group if true, the values of the returned array is
  2670.      *       wrapped in another array.  If the same key value (in the first
  2671.      *       column) repeats itself, the values will be appended to this array
  2672.      *       instead of overwriting the existing values.
  2673.      * @return mixed MDB2_OK or data array on success, a MDB2 error on failure
  2674.      * @access public
  2675.      */
  2676.     function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT,
  2677.         $rekey = false, $force_array = false, $group = false)
  2678.     {
  2679.         $result = $this->query($query, $types);
  2680.         if (!MDB2::isResultCommon($result)) {
  2681.             return $result;
  2682.         }
  2683.  
  2684.         $all = $result->fetchAll($fetchmode, $rekey, $force_array, $group);
  2685.         $result->free();
  2686.         return $all;
  2687.     }
  2688. }
  2689.  
  2690. class MDB2_Result
  2691. {
  2692. }
  2693.  
  2694. class MDB2_Result_Common extends MDB2_Result
  2695. {
  2696.     var $db;
  2697.     var $result;
  2698.     var $rownum = -1;
  2699.     var $types = array();
  2700.     var $values = array();
  2701.     var $offset;
  2702.     var $offset_count = 0;
  2703.     var $limit;
  2704.     var $column_names;
  2705.  
  2706.     // {{{ constructor
  2707.  
  2708.     /**
  2709.      * Constructor
  2710.      */
  2711.     function __construct(&$db, &$result, $limit = 0, $offset = 0)
  2712.     {
  2713.         $this->db =& $db;
  2714.         $this->result =& $result;
  2715.         $this->offset = $offset;
  2716.         $this->limit = max(0, $limit - 1);
  2717.     }
  2718.  
  2719.     function MDB2_Result_Common(&$db, &$result, $limit = 0, $offset = 0)
  2720.     {
  2721.         $this->__construct($db, $result, $limit, $offset);
  2722.     }
  2723.  
  2724.     // }}}
  2725.     // {{{ setResultTypes()
  2726.  
  2727.     /**
  2728.      * Define the list of types to be associated with the columns of a given
  2729.      * result set.
  2730.      *
  2731.      * This function may be called before invoking fetchRow(), fetchOne(),
  2732.      * fetchCol() and fetchAll() so that the necessary data type
  2733.      * conversions are performed on the data to be retrieved by them. If this
  2734.      * function is not called, the type of all result set columns is assumed
  2735.      * to be text, thus leading to not perform any conversions.
  2736.      *
  2737.      * @param string $types array variable that lists the
  2738.      *       data types to be expected in the result set columns. If this array
  2739.      *       contains less types than the number of columns that are returned
  2740.      *       in the result set, the remaining columns are assumed to be of the
  2741.      *       type text. Currently, the types clob and blob are not fully
  2742.      *       supported.
  2743.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  2744.      * @access public
  2745.      */
  2746.     function setResultTypes($types)
  2747.     {
  2748.         $load = $this->db->loadModule('Datatype', null, true);
  2749.         if (PEAR::isError($load)) {
  2750.             return $load;
  2751.         }
  2752.         $types = $this->db->datatype->checkResultTypes($types);
  2753.         if (PEAR::isError($types)) {
  2754.             return $types;
  2755.         }
  2756.         $this->types = $types;
  2757.         return MDB2_OK;
  2758.     }
  2759.  
  2760.     // }}}
  2761.     // {{{ seek()
  2762.  
  2763.     /**
  2764.      * seek to a specific row in a result set
  2765.      *
  2766.      * @param int    $rownum    number of the row where the data can be found
  2767.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  2768.      * @access public
  2769.      */
  2770.     function seek($rownum = 0)
  2771.     {
  2772.         $target_rownum = $rownum - 1;
  2773.         if ($this->rownum > $target_rownum) {
  2774.             return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2775.                 'seek: seeking to previous rows not implemented');
  2776.         }
  2777.         while ($this->rownum < $target_rownum) {
  2778.             $this->fetchRow();
  2779.         }
  2780.         return MDB2_OK;
  2781.     }
  2782.  
  2783.     // }}}
  2784.     // {{{ fetchRow()
  2785.  
  2786.     /**
  2787.      * Fetch and return a row of data
  2788.      *
  2789.      * @param int       $fetchmode  how the array data should be indexed
  2790.      * @param int    $rownum    number of the row where the data can be found
  2791.      * @return int data array on success, a MDB2 error on failure
  2792.      * @access public
  2793.      */
  2794.     function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
  2795.     {
  2796.         $err =& $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2797.             'fetch: method not implemented');
  2798.         return $err;
  2799.     }
  2800.  
  2801.     // }}}
  2802.     // {{{ fetchOne()
  2803.  
  2804.     /**
  2805.      * fetch single column from the first row from a result set
  2806.      *
  2807.      * @param int $colnum the column number to fetch
  2808.      * @return string data on success, a MDB2 error on failure
  2809.      * @access public
  2810.      */
  2811.     function fetchOne($colnum = 0)
  2812.     {
  2813.         $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC;
  2814.         $row = $this->fetchRow($fetchmode);
  2815.         if (!is_array($row) || PEAR::isError($row)) {
  2816.             return $row;
  2817.         }
  2818.         if (!array_key_exists($colnum, $row)) {
  2819.             return $this->db->raiseError(MDB2_ERROR_TRUNCATED);
  2820.         }
  2821.         return $row[$colnum];
  2822.     }
  2823.  
  2824.     // }}}
  2825.     // {{{ fetchCol()
  2826.  
  2827.     /**
  2828.      * Fetch and return a column of data (it uses current for that)
  2829.      *
  2830.      * @param int $colnum the column number to fetch
  2831.      * @return mixed data array on success, a MDB2 error on failure
  2832.      * @access public
  2833.      */
  2834.     function fetchCol($colnum = 0)
  2835.     {
  2836.         $column = array();
  2837.         $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC;
  2838.         $row = $this->fetchRow($fetchmode);
  2839.         if (is_array($row)) {
  2840.             if (!array_key_exists($colnum, $row)) {
  2841.                 return $this->db->raiseError(MDB2_ERROR_TRUNCATED);
  2842.             }
  2843.             do {
  2844.                 $column[] = $row[$colnum];
  2845.             } while (is_array($row = $this->fetchRow($fetchmode)));
  2846.         }
  2847.         if (PEAR::isError($row)) {
  2848.             return $row;
  2849.         }
  2850.         return $column;
  2851.     }
  2852.  
  2853.     // }}}
  2854.     // {{{ fetchAll()
  2855.  
  2856.     /**
  2857.      * Fetch and return a column of data (it uses fetchRow for that)
  2858.      *
  2859.      * @param int    $fetchmode  the fetch mode to use:
  2860.      *                            + MDB2_FETCHMODE_ORDERED
  2861.      *                            + MDB2_FETCHMODE_ASSOC
  2862.      *                            + MDB2_FETCHMODE_ORDERED | MDB2_FETCHMODE_FLIPPED
  2863.      *                            + MDB2_FETCHMODE_ASSOC | MDB2_FETCHMODE_FLIPPED
  2864.      * @param boolean $rekey if set to true, the $all will have the first
  2865.      *       column as its first dimension
  2866.      * @param boolean $force_array used only when the query returns exactly
  2867.      *       two columns. If true, the values of the returned array will be
  2868.      *       one-element arrays instead of scalars.
  2869.      * @param boolean $group if true, the values of the returned array is
  2870.      *       wrapped in another array.  If the same key value (in the first
  2871.      *       column) repeats itself, the values will be appended to this array
  2872.      *       instead of overwriting the existing values.
  2873.      * @return mixed data array on success, a MDB2 error on failure
  2874.      * @access public
  2875.      * @see getAssoc()
  2876.      */
  2877.     function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false,
  2878.         $force_array = false, $group = false)
  2879.     {
  2880.         $all = array();
  2881.         $row = $this->fetchRow($fetchmode);
  2882.         if (PEAR::isError($row)) {
  2883.             return $row;
  2884.         } elseif (!$row) {
  2885.             return $all;
  2886.         }
  2887.  
  2888.         $shift_array = $rekey ? false : null;
  2889.         if (!is_null($shift_array)) {
  2890.             if (is_object($row)) {
  2891.                 $colnum = count(get_object_vars($row));
  2892.             } else {
  2893.                 $colnum = count($row);
  2894.             }
  2895.             if ($colnum < 2) {
  2896.                 return $this->db->raiseError(MDB2_ERROR_TRUNCATED);
  2897.             }
  2898.             $shift_array = (!$force_array && $colnum == 2);
  2899.         }
  2900.  
  2901.         if ($rekey) {
  2902.             do {
  2903.                 if (is_object($row)) {
  2904.                     $arr = get_object_vars($row);
  2905.                     $key = reset($arr);
  2906.                     unset($row->{$key});
  2907.                 } else {
  2908.                     if ($fetchmode & MDB2_FETCHMODE_ASSOC) {
  2909.                         $key = reset($row);
  2910.                         unset($row[key($row)]);
  2911.                     } else {
  2912.                         $key = array_shift($row);
  2913.                     }
  2914.                     if ($shift_array) {
  2915.                         $row = array_shift($row);
  2916.                     }
  2917.                 }
  2918.                 if ($group) {
  2919.                     $all[$key][] = $row;
  2920.                 } else {
  2921.                     $all[$key] = $row;
  2922.                 }
  2923.             } while (($row = $this->fetchRow($fetchmode)));
  2924.         } elseif ($fetchmode & MDB2_FETCHMODE_FLIPPED) {
  2925.             do {
  2926.                 foreach ($row as $key => $val) {
  2927.                     $all[$key][] = $val;
  2928.                 }
  2929.             } while (($row = $this->fetchRow($fetchmode)));
  2930.         } else {
  2931.             do {
  2932.                 $all[] = $row;
  2933.             } while (($row = $this->fetchRow($fetchmode)));
  2934.         }
  2935.  
  2936.         return $all;
  2937.     }
  2938.  
  2939.     // }}}
  2940.     // {{{ rowCount()
  2941.  
  2942.     /**
  2943.      * returns the actual row number that was last fetched (count from 0)
  2944.      * @return integer
  2945.      */
  2946.     function rowCount()
  2947.     {
  2948.         return $this->rownum + 1;
  2949.     }
  2950.  
  2951.     // }}}
  2952.     // {{{ numRows()
  2953.  
  2954.     /**
  2955.      * returns the number of rows in a result object
  2956.      *
  2957.      * @return mixed MDB2 Error Object or the number of rows
  2958.      * @access public
  2959.      */
  2960.     function numRows()
  2961.     {
  2962.         return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2963.             'numRows: method not implemented');
  2964.     }
  2965.  
  2966.     // }}}
  2967.     // {{{ nextResult()
  2968.  
  2969.     /**
  2970.      * Move the internal result pointer to the next available result
  2971.      *
  2972.      * @param a valid result resource
  2973.      * @return true on success, false if there is no more result set or an error object on failure
  2974.      * @access public
  2975.      */
  2976.     function nextResult()
  2977.     {
  2978.         return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2979.             'nextResult: method not implemented');
  2980.     }
  2981.  
  2982.     // }}}
  2983.     // {{{ getColumnNames()
  2984.  
  2985.     /**
  2986.      * Retrieve the names of columns returned by the DBMS in a query result or
  2987.      * from the cache.
  2988.      *
  2989.      * @return mixed associative array variable
  2990.      *       that holds the names of columns. The indexes of the array are
  2991.      *       the column names mapped to lower case and the values are the
  2992.      *       respective numbers of the columns starting from 0. Some DBMS may
  2993.      *       not return any columns when the result set does not contain any
  2994.      *       rows.
  2995.      *      a MDB2 error on failure
  2996.      * @access public
  2997.      */
  2998.     function getColumnNames()
  2999.     {
  3000.         if (!isset($this->column_names)) {
  3001.             $result = $this->_getColumnNames();
  3002.             if (PEAR::isError($result)) {
  3003.                 return $result;
  3004.             }
  3005.             $this->column_names = $result;
  3006.         }
  3007.         return $this->column_names;
  3008.     }
  3009.  
  3010.     // }}}
  3011.     // {{{ _getColumnNames()
  3012.  
  3013.     /**
  3014.      * Retrieve the names of columns returned by the DBMS in a query result.
  3015.      *
  3016.      * @return mixed associative array variable
  3017.      *       that holds the names of columns. The indexes of the array are
  3018.      *       the column names mapped to lower case and the values are the
  3019.      *       respective numbers of the columns starting from 0. Some DBMS may
  3020.      *       not return any columns when the result set does not contain any
  3021.      *       rows.
  3022.      *      a MDB2 error on failure
  3023.      * @access private
  3024.      */
  3025.     function _getColumnNames()
  3026.     {
  3027.         return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  3028.             'getColumnNames: method not implemented');
  3029.     }
  3030.  
  3031.     // }}}
  3032.     // {{{ numCols()
  3033.  
  3034.     /**
  3035.      * Count the number of columns returned by the DBMS in a query result.
  3036.      *
  3037.      * @return mixed integer value with the number of columns, a MDB2 error
  3038.      *       on failure
  3039.      * @access public
  3040.      */
  3041.     function numCols()
  3042.     {
  3043.         return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  3044.             'numCols: method not implemented');
  3045.     }
  3046.  
  3047.     // }}}
  3048.     // {{{ getResource()
  3049.  
  3050.     /**
  3051.      * return the resource associated with the result object
  3052.      *
  3053.      * @return resource
  3054.      * @access public
  3055.      */
  3056.     function getResource()
  3057.     {
  3058.         return $this->result;
  3059.     }
  3060.  
  3061.     // }}}
  3062.     // {{{ bindColumn()
  3063.  
  3064.     /**
  3065.      * Set bind variable to a column.
  3066.      *
  3067.      * @param int $column
  3068.      * @param mixed $value
  3069.      * @param string $type specifies the type of the field
  3070.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  3071.      * @access public
  3072.      */
  3073.     function bindColumn($column, &$value, $type = null)
  3074.     {
  3075.         if (!is_numeric($column)) {
  3076.             $column_names = $this->getColumnNames();
  3077.             if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  3078.                 if ($this->db->options['field_case'] == CASE_LOWER) {
  3079.                     $column = strtolower($column);
  3080.                 } else {
  3081.                     $column = strtoupper($column);
  3082.                 }
  3083.             }
  3084.             $column = $column_names[$column];
  3085.         }
  3086.         $this->values[$column] =& $value;
  3087.         if (!is_null($type)) {
  3088.             $this->types[$column] = $type;
  3089.         }
  3090.         return MDB2_OK;
  3091.     }
  3092.  
  3093.     // }}}
  3094.     // {{{ _assignBindColumns()
  3095.  
  3096.     /**
  3097.      * Bind a variable to a value in the result row.
  3098.      *
  3099.      * @param array $row
  3100.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  3101.      * @access private
  3102.      */
  3103.     function _assignBindColumns($row)
  3104.     {
  3105.         $row = array_values($row);
  3106.         foreach ($row as $column => $value) {
  3107.             if (array_key_exists($column, $this->values)) {
  3108.                 $this->values[$column] = $value;
  3109.             }
  3110.         }
  3111.         return MDB2_OK;
  3112.     }
  3113.  
  3114.     // }}}
  3115.     // {{{ free()
  3116.  
  3117.     /**
  3118.      * Free the internal resources associated with result.
  3119.      *
  3120.      * @return boolean true on success, false if result is invalid
  3121.      * @access public
  3122.      */
  3123.     function free()
  3124.     {
  3125.         $this->result = false;
  3126.         return MDB2_OK;
  3127.     }
  3128. }
  3129.  
  3130. // }}}
  3131. // {{{ class MDB2_Row
  3132.  
  3133. /**
  3134.  * Pear MDB2 Row Object
  3135.  * @see MDB2_Driver_Common::setFetchMode()
  3136.  */
  3137. class MDB2_Row
  3138. {
  3139.     // {{{ constructor
  3140.  
  3141.     /**
  3142.      * constructor
  3143.      *
  3144.      * @param resource row data as array
  3145.      */
  3146.     function __construct(&$row)
  3147.     {
  3148.         foreach ($row as $key => $value) {
  3149.             $this->$key = &$row[$key];
  3150.         }
  3151.     }
  3152.  
  3153.     function MDB2_Row(&$row)
  3154.     {
  3155.         $this->__construct($row);
  3156.     }
  3157. }
  3158.  
  3159. class MDB2_Statement_Common
  3160. {
  3161.     var $db;
  3162.     var $statement;
  3163.     var $query;
  3164.     var $result_types;
  3165.     var $types;
  3166.     var $values = array();
  3167.     var $limit;
  3168.     var $offset;
  3169.     var $is_manip;
  3170.  
  3171.     // {{{ constructor
  3172.  
  3173.     /**
  3174.      * Constructor
  3175.      */
  3176.     function __construct(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
  3177.     {
  3178.         $this->db =& $db;
  3179.         $this->statement =& $statement;
  3180.         $this->positions = $positions;
  3181.         $this->query = $query;
  3182.         $this->types = (array)$types;
  3183.         $this->result_types = (array)$result_types;
  3184.         $this->limit = $limit;
  3185.         $this->is_manip = $is_manip;
  3186.         $this->offset = $offset;
  3187.     }
  3188.  
  3189.     function MDB2_Statement_Common(&$db, &$statement, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
  3190.     {
  3191.         $this->__construct($db, $statement, $query, $types, $result_types, $is_manip, $limit, $offset);
  3192.     }
  3193.  
  3194.     // }}}
  3195.     // {{{ bindParam()
  3196.  
  3197.     /**
  3198.      * Set the value of a parameter of a prepared query.
  3199.      *
  3200.      * @param int $parameter the order number of the parameter in the query
  3201.      *       statement. The order number of the first parameter is 1.
  3202.      * @param mixed $value value that is meant to be assigned to specified
  3203.      *       parameter. The type of the value depends on the $type argument.
  3204.      * @param string $type specifies the type of the field
  3205.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  3206.      * @access public
  3207.      */
  3208.     function bindParam($parameter, &$value, $type = null)
  3209.     {
  3210.         if (!is_numeric($parameter)) {
  3211.             $parameter = preg_replace('/^:(.*)$/', '\\1', $parameter);
  3212.         }
  3213.         if (!array_key_exists($parameter, $this->positions)) {
  3214.             return $this->db->raiseError();
  3215.         }
  3216.         $this->values[$parameter] =& $value;
  3217.         if (!is_null($type)) {
  3218.             $this->types[$parameter] = $type;
  3219.         }
  3220.         return MDB2_OK;
  3221.     }
  3222.  
  3223.     // }}}
  3224.     // {{{ bindParamArray()
  3225.  
  3226.     /**
  3227.      * Set the values of multiple a parameter of a prepared query in bulk.
  3228.      *
  3229.      * @param array $values array thats specifies all necessary infromation
  3230.      *       for bindParam() the array elements must use keys corresponding to
  3231.      *       the number of the position of the parameter.
  3232.      * @param array $types specifies the types of the fields
  3233.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  3234.      * @access public
  3235.      * @see bindParam()
  3236.      */
  3237.     function bindParamArray(&$values, $types = null)
  3238.     {
  3239.         $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null);
  3240.         $parameters = array_keys($values);
  3241.         foreach ($parameters as $key => $parameter) {
  3242.             $this->bindParam($parameter, $values[$parameter], $types[$key]);
  3243.         }
  3244.         return MDB2_OK;
  3245.     }
  3246.  
  3247.     // }}}
  3248.     // {{{ execute()
  3249.  
  3250.     /**
  3251.      * Execute a prepared query statement.
  3252.      *
  3253.      * @param array $values array thats specifies all necessary infromation
  3254.      *       for bindParam() the array elements must use keys corresponding to
  3255.      *       the number of the position of the parameter.
  3256.      * @param mixed $result_class string which specifies which result class to use
  3257.      * @param mixed $result_wrap_class string which specifies which class to wrap results in
  3258.      * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
  3259.      * @access public
  3260.      */
  3261.     function &execute($values = null, $result_class = true, $result_wrap_class = false)
  3262.     {
  3263.         if (!empty($values)) {
  3264.             $this->bindParamArray($values);
  3265.         }
  3266.         $result =& $this->_execute($result_class, $result_wrap_class);
  3267.         if (is_numeric($result)) {
  3268.             $this->rownum = $result - 1;
  3269.         }
  3270.         return $result;
  3271.     }
  3272.  
  3273.     // }}}
  3274.     // {{{ _execute()
  3275.  
  3276.     /**
  3277.      * Execute a prepared query statement helper method.
  3278.      *
  3279.      * @param mixed $result_class string which specifies which result class to use
  3280.      * @param mixed $result_wrap_class string which specifies which class to wrap results in
  3281.      * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
  3282.      * @access private
  3283.      */
  3284.     function &_execute($result_class = true, $result_wrap_class = false)
  3285.     {
  3286.         $query = '';
  3287.         $last_position = 0;
  3288.         foreach ($this->positions as $parameter => $current_position) {
  3289.             if (!array_key_exists($parameter, $this->values)) {
  3290.                 return $this->db->raiseError();
  3291.             }
  3292.             $value = $this->values[$parameter];
  3293.             $query.= substr($this->query, $last_position, $current_position - $last_position);
  3294.             if (!isset($value)) {
  3295.                 $value_quoted = 'NULL';
  3296.             } else {
  3297.                 $type = array_key_exists($parameter, $this->types) ? $this->types[$parameter] : null;
  3298.                 $value_quoted = $this->db->quote($value, $type);
  3299.                 if (PEAR::isError($value_quoted)) {
  3300.                     return $value_quoted;
  3301.                 }
  3302.             }
  3303.             $query.= $value_quoted;
  3304.             $last_position = $current_position + 1;
  3305.         }
  3306.         $query.= substr($this->query, $last_position);
  3307.  
  3308.         $this->db->offset = $this->offset;
  3309.         $this->db->limit = $this->limit;
  3310.         if ($this->is_manip) {
  3311.             $result = $this->db->exec($query);
  3312.         } else {
  3313.             $result =& $this->db->query($query, $this->result_types, $result_class, $result_wrap_class);
  3314.         }
  3315.         return $result;
  3316.     }
  3317.  
  3318.     // }}}
  3319.     // {{{ free()
  3320.  
  3321.     /**
  3322.      * Release resources allocated for the specified prepared query.
  3323.      *
  3324.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  3325.      * @access public
  3326.      */
  3327.     function free()
  3328.     {
  3329.         return MDB2_OK;
  3330.     }
  3331. }
  3332.  
  3333. class MDB2_Module_Common
  3334. {
  3335.     /**
  3336.      * contains the key to the global MDB2 instance array of the associated
  3337.      * MDB2 instance
  3338.      *
  3339.      * @var integer
  3340.      * @access protected
  3341.      */
  3342.     var $db_index;
  3343.  
  3344.     // {{{ constructor
  3345.  
  3346.     /**
  3347.      * Constructor
  3348.      */
  3349.     function __construct($db_index)
  3350.     {
  3351.         $this->db_index = $db_index;
  3352.     }
  3353.  
  3354.     function MDB2_Module_Common($db_index)
  3355.     {
  3356.         $this->__construct($db_index);
  3357.     }
  3358.  
  3359.     // }}}
  3360.     // {{{ getDBInstance()
  3361.  
  3362.     /**
  3363.      * get the instance of MDB2 associated with the module instance
  3364.      *
  3365.      * @return object MDB2 instance or a MDB2 error on failure
  3366.      * @access public
  3367.      */
  3368.     function &getDBInstance()
  3369.     {
  3370.         if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
  3371.             $result =& $GLOBALS['_MDB2_databases'][$this->db_index];
  3372.         } else {
  3373.             $result =& MDB2::raiseError(MDB2_ERROR, null, null, 'could not find MDB2 instance');
  3374.         }
  3375.         return $result;
  3376.     }
  3377. }
  3378.  
  3379. // }}}
  3380. // {{{ MDB2_closeOpenTransactions()
  3381.  
  3382. /**
  3383.  * close any open transactions form persistant connections
  3384.  *
  3385.  * @return void
  3386.  * @access public
  3387.  */
  3388. function MDB2_closeOpenTransactions()
  3389. {
  3390.     reset($GLOBALS['_MDB2_databases']);
  3391.     while (next($GLOBALS['_MDB2_databases'])) {
  3392.         $key = key($GLOBALS['_MDB2_databases']);
  3393.         if ($GLOBALS['_MDB2_databases'][$key]->opened_persistent
  3394.             && $GLOBALS['_MDB2_databases'][$key]->in_transaction
  3395.         ) {
  3396.             $GLOBALS['_MDB2_databases'][$key]->rollback();
  3397.         }
  3398.     }
  3399. }
  3400.  
  3401. // }}}
  3402. // {{{ MDB2_defaultDebugOutput()
  3403.  
  3404. /**
  3405.  * default debug output handler
  3406.  *
  3407.  * @param object $db reference to an MDB2 database object
  3408.  * @param string $message message that should be appended to the debug
  3409.  *       variable
  3410.  * @return string the corresponding error message, of false
  3411.  * if the error code was unknown
  3412.  * @access public
  3413.  */
  3414. function MDB2_defaultDebugOutput(&$db, $scope, $message, $is_manip = null)
  3415. {
  3416.     $db->debug_output.= $scope.'('.$db->db_index.'): ';
  3417.     $db->debug_output.= $message.$db->getOption('log_line_break');
  3418. }
  3419.  
  3420. ?>